[pypy-commit] cffi default: Improve the error message for getattr/setattr

arigo pypy.commits at gmail.com
Tue Jan 24 18:25:40 EST 2017


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r2873:8f9136ac88e6
Date: 2017-01-25 00:25 +0100
http://bitbucket.org/cffi/cffi/changeset/8f9136ac88e6/

Log:	Improve the error message for getattr/setattr

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -455,6 +455,8 @@
 static PyObject *
 get_field_name(CTypeDescrObject *ct, CFieldObject *cf);   /* forward */
 
+/* returns 0 if the struct ctype is opaque, 1 if it is not, or -1 if
+   an exception occurs */
 #define force_lazy_struct(ct)                                           \
     ((ct)->ct_stuff != NULL ? 1 : do_realize_lazy_struct(ct))
 
@@ -2452,11 +2454,26 @@
     return _cdata_add_or_sub(v, w, -1);
 }
 
+static void
+_cdata_attr_errmsg(char *errmsg, CDataObject *cd, PyObject *attr)
+{
+    char *text;
+    if (!PyErr_ExceptionMatches(PyExc_AttributeError))
+        return;
+    PyErr_Clear();
+    text = PyText_AsUTF8(attr);
+    if (text == NULL)
+        return;
+    PyErr_Format(PyExc_AttributeError, errmsg, cd->c_type->ct_name, text);
+}
+
 static PyObject *
 cdata_getattro(CDataObject *cd, PyObject *attr)
 {
     CFieldObject *cf;
     CTypeDescrObject *ct = cd->c_type;
+    char *errmsg = "cdata '%s' has no attribute '%s'";
+    PyObject *x;
 
     if (ct->ct_flags & CT_POINTER)
         ct = ct->ct_itemdescr;
@@ -2488,14 +2505,19 @@
                 return new_simple_cdata(data,
                     (CTypeDescrObject *)cf->cf_type->ct_stuff);
             }
+            errmsg = "cdata '%s' has no field '%s'";
             break;
         case -1:
             return NULL;
         default:
+            errmsg = "cdata '%s' points to an opaque type: cannot read fields";
             break;
         }
     }
-    return PyObject_GenericGetAttr((PyObject *)cd, attr);
+    x = PyObject_GenericGetAttr((PyObject *)cd, attr);
+    if (x == NULL)
+        _cdata_attr_errmsg(errmsg, cd, attr);
+    return x;
 }
 
 static int
@@ -2503,6 +2525,8 @@
 {
     CFieldObject *cf;
     CTypeDescrObject *ct = cd->c_type;
+    char *errmsg = "cdata '%s' has no attribute '%s'";
+    int x;
 
     if (ct->ct_flags & CT_POINTER)
         ct = ct->ct_itemdescr;
@@ -2522,14 +2546,19 @@
                     return -1;
                 }
             }
+            errmsg = "cdata '%s' has no field '%s'";
             break;
         case -1:
             return -1;
         default:
+            errmsg = "cdata '%s' points to an opaque type: cannot write fields";
             break;
         }
     }
-    return PyObject_GenericSetAttr((PyObject *)cd, attr, value);
+    x = PyObject_GenericSetAttr((PyObject *)cd, attr, value);
+    if (x < 0)
+        _cdata_attr_errmsg(errmsg, cd, attr);
+    return x;
 }
 
 static PyObject *
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -748,8 +748,14 @@
     BInt = new_primitive_type("int")
     BStruct = new_struct_type("struct foo")
     BStructPtr = new_pointer_type(BStruct)
-    p = cast(BStructPtr, 0)
-    py.test.raises(AttributeError, "p.a1")    # opaque
+    p = cast(BStructPtr, 42)
+    e = py.test.raises(AttributeError, "p.a1")    # opaque
+    assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: "
+                            "cannot read fields")
+    e = py.test.raises(AttributeError, "p.a1 = 10")    # opaque
+    assert str(e.value) == ("cdata 'struct foo *' points to an opaque type: "
+                            "cannot write fields")
+
     complete_struct_or_union(BStruct, [('a1', BInt, -1),
                                        ('a2', BInt, -1)])
     p = newp(BStructPtr, None)
@@ -760,8 +766,24 @@
     assert s.a2 == 123
     py.test.raises(OverflowError, "s.a1 = sys.maxsize+1")
     assert s.a1 == 0
-    py.test.raises(AttributeError, "p.foobar")
-    py.test.raises(AttributeError, "s.foobar")
+    e = py.test.raises(AttributeError, "p.foobar")
+    assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'"
+    e = py.test.raises(AttributeError, "p.foobar = 42")
+    assert str(e.value) == "cdata 'struct foo *' has no field 'foobar'"
+    e = py.test.raises(AttributeError, "s.foobar")
+    assert str(e.value) == "cdata 'struct foo' has no field 'foobar'"
+    e = py.test.raises(AttributeError, "s.foobar = 42")
+    assert str(e.value) == "cdata 'struct foo' has no field 'foobar'"
+    j = cast(BInt, 42)
+    e = py.test.raises(AttributeError, "j.foobar")
+    assert str(e.value) == "cdata 'int' has no attribute 'foobar'"
+    e = py.test.raises(AttributeError, "j.foobar = 42")
+    assert str(e.value) == "cdata 'int' has no attribute 'foobar'"
+    j = cast(new_pointer_type(BInt), 42)
+    e = py.test.raises(AttributeError, "j.foobar")
+    assert str(e.value) == "cdata 'int *' has no attribute 'foobar'"
+    e = py.test.raises(AttributeError, "j.foobar = 42")
+    assert str(e.value) == "cdata 'int *' has no attribute 'foobar'"
 
 def test_union_instance():
     BInt = new_primitive_type("int")


More information about the pypy-commit mailing list