1. Python CFFI
  2. Untitled project
  3. cffi

Commits

Armin Rigo  committed d63ceb2

Kill again ffi.inspecttype(), and replace it with read-only attributes
on the ctype objects themselves.

  • Participants
  • Parent commits bde32d4
  • Branches default

Comments (0)

Files changed (5)

File c/_cffi_backend.c

View file
     return 0;
 }
 
+
+static PyObject *nosuchattr(const char *attr)
+{
+    PyErr_SetString(PyExc_AttributeError, attr);
+    return NULL;
+}
+
+static PyObject *ctypeget_kind(CTypeDescrObject *ct, void *context)
+{
+    char *result;
+    if (ct->ct_flags & CT_PRIMITIVE_ANY) {
+        if (ct->ct_flags & CT_IS_ENUM)
+            result = "enum";
+        else
+            result = "primitive";
+    }
+    else if (ct->ct_flags & CT_POINTER) {
+        result = "pointer";
+    }
+    else if (ct->ct_flags & CT_ARRAY) {
+        result = "array";
+    }
+    else if (ct->ct_flags & CT_VOID) {
+        result = "void";
+    }
+    else if (ct->ct_flags & CT_STRUCT) {
+        result = "struct";
+    }
+    else if (ct->ct_flags & CT_UNION) {
+        result = "union";
+    }
+    else if (ct->ct_flags & CT_FUNCTIONPTR) {
+        result = "function";
+    }
+    else
+        result = "?";
+
+    return PyText_FromString(result);
+}
+
+static PyObject *ctypeget_cname(CTypeDescrObject *ct, void *context)
+{
+    return PyText_FromString(ct->ct_name);
+}
+
+static PyObject *ctypeget_item(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & (CT_POINTER | CT_ARRAY)) {
+        Py_INCREF(ct->ct_itemdescr);
+        return (PyObject *)ct->ct_itemdescr;
+    }
+    return nosuchattr("item");
+}
+
+static PyObject *ctypeget_length(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_ARRAY) {
+        if (ct->ct_length >= 0) {
+            return PyInt_FromSsize_t(ct->ct_length);
+        }
+        else {
+            Py_INCREF(Py_None);
+            return Py_None;
+        }
+    }
+    return nosuchattr("length");
+}
+
+static PyObject *
+get_field_name(CTypeDescrObject *ct, CFieldObject *cf);   /* forward */
+
+static PyObject *ctypeget_fields(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & (CT_STRUCT | CT_UNION)) {
+        if (!(ct->ct_flags & CT_IS_OPAQUE)) {
+            CFieldObject *cf;
+            PyObject *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 res;
+        }
+        else {
+            Py_INCREF(Py_None);
+            return Py_None;
+        }
+    }
+    return nosuchattr("fields");
+}
+
+static PyObject *ctypeget_args(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *t = ct->ct_stuff;
+        return PyTuple_GetSlice(t, 2, PyTuple_GET_SIZE(t));
+    }
+    return nosuchattr("args");
+}
+
+static PyObject *ctypeget_result(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1);
+        Py_XINCREF(res);
+        return res;
+    }
+    return nosuchattr("result");
+}
+
+static PyObject *ctypeget_ellipsis(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *res = ct->ct_extra ? Py_False : Py_True;
+        Py_INCREF(res);
+        return res;
+    }
+    return nosuchattr("ellipsis");
+}
+
+static PyObject *ctypeget_abi(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_FUNCTIONPTR) {
+        PyObject *res = PyTuple_GetItem(ct->ct_stuff, 0);
+        Py_XINCREF(res);
+        return res;
+    }
+    return nosuchattr("abi");
+}
+
+static PyObject *ctypeget_elements(CTypeDescrObject *ct, void *context)
+{
+    if (ct->ct_flags & CT_IS_ENUM) {
+        PyObject *res = PyTuple_GetItem(ct->ct_stuff, 1);
+        Py_XINCREF(res);
+        return res;
+    }
+    return nosuchattr("elements");
+}
+
+static PyGetSetDef ctypedescr_getsets[] = {
+    {"kind", (getter)ctypeget_kind, NULL, "kind"},
+    {"cname", (getter)ctypeget_cname, NULL, "C name"},
+    {"item", (getter)ctypeget_item, NULL, "pointer to, or array of"},
+    {"length", (getter)ctypeget_length, NULL, "array length or None"},
+    {"fields", (getter)ctypeget_fields, NULL, "struct or union fields"},
+    {"args", (getter)ctypeget_args, NULL, "function argument types"},
+    {"result", (getter)ctypeget_result, NULL, "function result type"},
+    {"ellipsis", (getter)ctypeget_ellipsis, NULL, "function has '...'"},
+    {"abi", (getter)ctypeget_abi, NULL, "function ABI"},
+    {"elements", (getter)ctypeget_elements, NULL, "enum elements"},
+    {NULL}                        /* sentinel */
+};
+
+static PyObject *
+ctypedescr_dir(PyObject *ct, PyObject *noarg)
+{
+    int err;
+    struct PyGetSetDef *gsdef;
+    PyObject *res = PyList_New(0);
+    if (res == NULL)
+        return NULL;
+
+    for (gsdef = ctypedescr_getsets; gsdef->name; gsdef++) {
+        PyObject *x = PyObject_GetAttrString(ct, gsdef->name);
+        if (x == NULL) {
+            PyErr_Clear();
+        }
+        else {
+            Py_DECREF(x);
+            x = PyText_FromString(gsdef->name);
+            err = (x != NULL) ? PyList_Append(res, x) : -1;
+            Py_XDECREF(x);
+            if (err < 0) {
+                Py_DECREF(res);
+                return NULL;
+            }
+        }
+    }
+    return res;
+}
+
+static PyMethodDef ctypedescr_methods[] = {
+    {"__dir__",   ctypedescr_dir,  METH_NOARGS},
+    {NULL,        NULL}           /* sentinel */
+};
+
 static PyTypeObject CTypeDescr_Type = {
     PyVarObject_HEAD_INIT(NULL, 0)
     "_cffi_backend.CTypeDescr",
     (inquiry)ctypedescr_clear,                  /* tp_clear */
     0,                                          /* tp_richcompare */
     offsetof(CTypeDescrObject, ct_weakreflist), /* tp_weaklistoffset */
+    0,                                          /* tp_iter */
+    0,                                          /* tp_iternext */
+    ctypedescr_methods,                         /* tp_methods */
+    0,                                          /* tp_members */
+    ctypedescr_getsets,                         /* tp_getset */
 };
 
 /************************************************************/
         goto retry;
     }
     else {
-        PyErr_Format(PyExc_TypeError, "ctype '%s' is of unknown alignment",
+        PyErr_Format(PyExc_ValueError, "ctype '%s' is of unknown alignment",
                      ct->ct_name);
         return -1;
     }
     return NULL;
 }
 
-static PyObject *b_inspecttype(PyObject *self, PyObject *arg)
-{
-    CTypeDescrObject *ct = (CTypeDescrObject *)arg;
-
-    if (!CTypeDescr_Check(arg)) {
-        PyErr_SetString(PyExc_TypeError,"expected a 'ctype' object");
-        return NULL;
-    }
-    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);
-        }
-    }
-    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);
-        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 {
     Py_ssize_t nb_bytes;
     char *bufferp;
     {"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},
-    {"inspecttype", b_inspecttype, METH_O},
     {"newp", b_newp, METH_VARARGS},
     {"cast", b_cast, METH_VARARGS},
     {"callback", b_callback, METH_VARARGS},

File c/test_c.py

View file
     p = new_primitive_type("signed char")
     assert repr(p) == "<ctype 'signed char'>"
 
+def check_dir(p, expected):
+    got = set(name for name in dir(p) if not name.startswith('_'))
+    assert got == set(expected)
+
 def test_inspect_primitive_type():
     p = new_primitive_type("signed char")
-    assert inspecttype(p) == ("primitive", "signed char")
+    assert p.kind == "primitive"
+    assert p.cname == "signed char"
+    check_dir(p, ['cname', 'kind'])
 
 def test_cast_to_signed_char():
     p = new_primitive_type("signed char")
 def test_inspect_pointer_type():
     p1 = new_primitive_type("int")
     p2 = new_pointer_type(p1)
-    assert inspecttype(p2) == ("pointer", p1)
+    assert p2.kind == "pointer"
+    assert p2.cname == "int *"
+    assert p2.item is p1
+    check_dir(p2, ['cname', 'kind', 'item'])
     p3 = new_pointer_type(p2)
-    assert inspecttype(p3) == ("pointer", p2)
+    assert p3.item is p2
 
 def test_pointer_to_int():
     BInt = new_primitive_type("int")
 
 def test_void_type():
     p = new_void_type()
-    assert inspecttype(p) == ("void",)
+    assert p.kind == "void"
+    assert p.cname == "void"
+    check_dir(p, ['kind', 'cname'])
 
 def test_array_type():
     p = new_primitive_type("int")
 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)
+    assert p1.kind == "array"
+    assert p1.cname == "int[]"
+    assert p1.item is p
+    assert p1.length is None
+    check_dir(p1, ['cname', 'kind', 'item', 'length'])
     p1 = new_array_type(new_pointer_type(p), 42)
-    assert inspecttype(p1) == ("array", p, 42)
+    assert p1.kind == "array"
+    assert p1.cname == "int[42]"
+    assert p1.item is p
+    assert p1.length == 42
+    check_dir(p1, ['cname', 'kind', 'item', 'length'])
 
 def test_array_instance():
     LENGTH = 1423
     assert repr(BStruct) == "<ctype 'struct foo'>"
     BPtr = new_pointer_type(BStruct)
     assert repr(BPtr) == "<ctype 'struct foo *'>"
-    py.test.raises(TypeError, alignof, BStruct)
+    py.test.raises(ValueError, sizeof, BStruct)
+    py.test.raises(ValueError, alignof, BStruct)
 
 def test_new_union_type():
     BUnion = new_union_type("foo")
     BChar = new_primitive_type("char")
     BShort = new_primitive_type("short")
     BStruct = new_struct_type("foo")
-    assert inspecttype(BStruct) == ("struct", "foo", None, None, None)
+    assert BStruct.kind == "struct"
+    assert BStruct.cname == "struct foo"
+    assert BStruct.fields is None
+    check_dir(BStruct, ['cname', 'kind', 'fields'])
+    #
     complete_struct_or_union(BStruct, [('a1', BLong, -1),
                                        ('a2', BChar, -1),
                                        ('a3', BShort, -1)])
-    k, n, d, s, a = inspecttype(BStruct)
-    assert k == "struct" and n == "foo"
-    assert s == sizeof(BLong) + 2 * sizeof(BShort)
-    assert a == sizeof(BLong)
+    d = BStruct.fields
     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 inspecttype(BUnion) == ("union", "foo", None, None, None)
+    assert BUnion.kind == "union"
+    assert BUnion.cname == "union foo"
+    assert BUnion.fields is None
     complete_struct_or_union(BUnion, [('a1', BLong, -1),
                                       ('a2', BChar, -1)])
-    k, n, d, s, a = inspecttype(BUnion)
-    assert k == "union" and n == "foo"
-    assert s == a == sizeof(BLong)
+    d = BUnion.fields
     assert len(d) == 2
     assert d[0][0] == 'a1'
     assert d[0][1].type is BLong
 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)
+    assert BFunc.kind == "function"
+    assert BFunc.cname == "int(*)(int, int)"
+    assert BFunc.args == (BInt, BInt)
+    assert BFunc.result is BInt
+    assert BFunc.ellipsis is False
+    assert BFunc.abi == FFI_DEFAULT_ABI
 
 def test_function_type_taking_struct():
     BChar = new_primitive_type("char")
 def test_enum_type():
     BEnum = new_enum_type("foo", (), ())
     assert repr(BEnum) == "<ctype 'enum foo'>"
-    assert inspecttype(BEnum) == ("enum", [])
+    assert BEnum.kind == "enum"
+    assert BEnum.cname == "enum foo"
+    assert BEnum.elements == {}
     #
     BEnum = new_enum_type("foo", ('def', 'c', 'ab'), (0, 1, -20))
-    assert inspecttype(BEnum) == ("enum", [(-20, 'ab'), (0, 'def'), (1, 'c')])
+    assert BEnum.kind == "enum"
+    assert BEnum.elements == {-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 = inspecttype(BStruct)[2]
+    d = BStruct.fields
     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
     py.test.raises(TypeError, "x - 1")
 
 def test_void_errors():
-    py.test.raises(TypeError, alignof, new_void_type())
+    py.test.raises(ValueError, alignof, new_void_type())
     py.test.raises(TypeError, newp, new_pointer_type(new_void_type()), None)
     x = cast(new_pointer_type(new_void_type()), 42)
     py.test.raises(TypeError, "x + 1")
                                        ('a3', BChar, -1)])
     assert sizeof(BInnerStruct) == sizeof(BInt) * 2   # with alignment
     assert sizeof(BStruct) == sizeof(BInt) * 3        # 'a3' is placed after
-    d = inspecttype(BStruct)[2]
+    d = BStruct.fields
     assert len(d) == 3
     assert d[0][0] == 'a1'
     assert d[0][1].type is BInt

File cffi/api.py

View file
                 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

File doc/source/index.rst

View file
    as ``<cdata>`` and ``<ctype>`` in the rest of this document.  Note
    that some cdata objects may be actually of a subclass of
    ``ffi.CData``, and similarly with ctype, so you should check with
-   ``if isinstance(x, ffi.CData)``.
+   ``if isinstance(x, ffi.CData)``.  Also, ``<ctype>`` objects have
+   a number of attributes for introspection: ``kind`` and ``cname`` are
+   always present, and depending on the kind they may also have
+   ``item``, ``length``, ``fields``, ``args``, ``result``, ``ellipsis``,
+   ``abi`` and ``elements``.
 
 ``ffi.sizeof("C type" or cdata object)``: return the size of the
 argument in bytes.  The argument can be either a C type, or a cdata object,
 
 .. "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
 ----------------------

File testing/test_ffi_backend.py

View file
 
     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)
+        assert ffi.typeof("long").kind == "primitive"
+        assert ffi.typeof("long(*)(long, long**, ...)").cname == (
+            "long(*)(long, long * *, ...)")
+        assert ffi.typeof("long(*)(long, long**, ...)").ellipsis is True