[pypy-commit] cffi default: Issue #379

arigo pypy.commits at gmail.com
Thu Aug 30 06:26:27 EDT 2018


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r3151:abb60e05d3bf
Date: 2018-08-30 11:36 +0200
http://bitbucket.org/cffi/cffi/changeset/abb60e05d3bf/

Log:	Issue #379

	Accept ``ffi.new("int[4]", p)`` if p is itself another cdata
	``int[4]``.

diff --git a/c/_cffi_backend.c b/c/_cffi_backend.c
--- a/c/_cffi_backend.c
+++ b/c/_cffi_backend.c
@@ -1228,17 +1228,17 @@
     return (cffi_char32_t)-1;
 }
 
-static int _convert_error(PyObject *init, const char *ct_name,
+static int _convert_error(PyObject *init, CTypeDescrObject *ct,
                           const char *expected)
 {
     if (CData_Check(init)) {
-        const char *ct_name_2 = ((CDataObject *)init)->c_type->ct_name;
-        if (strcmp(ct_name, ct_name_2) != 0)
+        CTypeDescrObject *ct2 = ((CDataObject *)init)->c_type;
+        if (strcmp(ct->ct_name, ct2->ct_name) != 0)
             PyErr_Format(PyExc_TypeError,
                          "initializer for ctype '%s' must be a %s, "
                          "not cdata '%s'",
-                         ct_name, expected, ct_name_2);
-        else {
+                         ct->ct_name, expected, ct2->ct_name);
+        else if (ct != ct2) {
             /* in case we'd give the error message "initializer for
                ctype 'A' must be a pointer to same type, not cdata
                'B'", but with A=B, then give instead a different error
@@ -1247,14 +1247,21 @@
                          "initializer for ctype '%s' appears indeed to be '%s',"
                          " but the types are different (check that you are not"
                          " e.g. mixing up different ffi instances)",
-                         ct_name, ct_name_2);
+                         ct->ct_name, ct2->ct_name);
+        }
+        else
+        {
+            PyErr_Format(PyExc_SystemError,
+                         "initializer for ctype '%s' is correct, but we get "
+                         "an internal mismatch--please report a bug",
+                         ct->ct_name);
         }
     }
     else
         PyErr_Format(PyExc_TypeError,
                      "initializer for ctype '%s' must be a %s, "
                      "not %.200s",
-                     ct_name, expected, Py_TYPE(init)->tp_name);
+                     ct->ct_name, expected, Py_TYPE(init)->tp_name);
     return -1;
 }
 
@@ -1368,6 +1375,15 @@
     return 0;
 }
 
+static Py_ssize_t
+get_array_length(CDataObject *cd)
+{
+    if (cd->c_type->ct_length < 0)
+        return ((CDataObject_own_length *)cd)->length;
+    else
+        return cd->c_type->ct_length;
+}
+
 static int
 convert_array_from_object(char *data, CTypeDescrObject *ct, PyObject *init)
 {
@@ -1453,13 +1469,24 @@
     }
 
  cannot_convert:
-    return _convert_error(init, ct->ct_name, expected);
+    if (CData_Check(init))
+    {
+        CDataObject *cd = (CDataObject *)init;
+        if (cd->c_type == ct)
+        {
+            Py_ssize_t n = get_array_length(cd);
+            memmove(data, cd->c_data, n * ctitem->ct_size);
+            return 0;
+        }
+    }
+    return _convert_error(init, ct, expected);
 }
 
 static int
 convert_struct_from_object(char *data, CTypeDescrObject *ct, PyObject *init,
                            Py_ssize_t *optvarsize)
 {
+    /* does not accept 'init' being already a CData */
     const char *expected;
 
     if (force_lazy_struct(ct) <= 0) {
@@ -1506,7 +1533,7 @@
     }
     expected = optvarsize == NULL ? "list or tuple or dict or struct-cdata"
                                   : "list or tuple or dict";
-    return _convert_error(init, ct->ct_name, expected);
+    return _convert_error(init, ct, expected);
 }
 
 #ifdef __GNUC__
@@ -1682,7 +1709,7 @@
     return _convert_overflow(init, ct->ct_name);
 
  cannot_convert:
-    return _convert_error(init, ct->ct_name, expected);
+    return _convert_error(init, ct, expected);
 }
 
 static int
@@ -1742,15 +1769,6 @@
     return 0;
 }
 
-static Py_ssize_t
-get_array_length(CDataObject *cd)
-{
-    if (cd->c_type->ct_length < 0)
-        return ((CDataObject_own_length *)cd)->length;
-    else
-        return cd->c_type->ct_length;
-}
-
 static int
 get_alignment(CTypeDescrObject *ct)
 {
diff --git a/c/test_c.py b/c/test_c.py
--- a/c/test_c.py
+++ b/c/test_c.py
@@ -1873,7 +1873,7 @@
 
 def test_newp_copying():
     """Test that we can do newp(<type>, <cdata of the given type>) for most
-    types, with the exception of arrays, like in C.
+    types, including same-type arrays.
     """
     BInt = new_primitive_type("int")
     p = newp(new_pointer_type(BInt), cast(BInt, 42))
@@ -1902,8 +1902,9 @@
     a1 = newp(BArray, [1, 2, 3, 4])
     py.test.raises(TypeError, newp, BArray, a1)
     BArray6 = new_array_type(new_pointer_type(BInt), 6)
-    a1 = newp(BArray6, None)
-    py.test.raises(TypeError, newp, BArray6, a1)
+    a1 = newp(BArray6, [10, 20, 30])
+    a2 = newp(BArray6, a1)
+    assert list(a2) == [10, 20, 30, 0, 0, 0]
     #
     s1 = newp(BStructPtr, [42])
     s2 = newp(BStructPtr, s1[0])
diff --git a/cffi/backend_ctypes.py b/cffi/backend_ctypes.py
--- a/cffi/backend_ctypes.py
+++ b/cffi/backend_ctypes.py
@@ -636,6 +636,10 @@
                 if isinstance(init, bytes):
                     init = [init[i:i+1] for i in range(len(init))]
                 else:
+                    if isinstance(init, CTypesGenericArray):
+                        if (len(init) != len(blob) or
+                            not isinstance(init, CTypesArray)):
+                            raise TypeError("length/type mismatch: %s" % (init,))
                     init = tuple(init)
                 if len(init) > len(blob):
                     raise IndexError("too many initializers")
diff --git a/testing/cffi0/backend_tests.py b/testing/cffi0/backend_tests.py
--- a/testing/cffi0/backend_tests.py
+++ b/testing/cffi0/backend_tests.py
@@ -1972,3 +1972,18 @@
         assert seen[1] == 101
         assert seen[2] == 202
         assert seen[3] == 303
+
+    def test_ffi_array_as_init(self):
+        ffi = FFI(backend=self.Backend())
+        p = ffi.new("int[4]", [10, 20, 30, 400])
+        q = ffi.new("int[4]", p)
+        assert list(q) == [10, 20, 30, 400]
+        py.test.raises(TypeError, ffi.new, "int[3]", p)
+        py.test.raises(TypeError, ffi.new, "int[5]", p)
+        py.test.raises(TypeError, ffi.new, "int16_t[4]", p)
+        s = ffi.new("struct {int i[4];}*", {'i': p})
+        assert list(s.i) == [10, 20, 30, 400]
+
+    def test_too_many_initializers(self):
+        ffi = FFI(backend=self.Backend())
+        py.test.raises(IndexError, ffi.new, "int[4]", [10, 20, 30, 40, 50])


More information about the pypy-commit mailing list