[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