mirror of https://github.com/python/cpython
Issue #25498: Fix GC crash due to ctypes objects wrapping a memoryview
This was a regression caused by revision 1da9630e9b7f. Based on patch by Eryksun.
This commit is contained in:
parent
abe9625eeb
commit
1bb651540e
|
@ -38,11 +38,32 @@ class Test(unittest.TestCase):
|
|||
del a; gc.collect(); gc.collect(); gc.collect()
|
||||
self.assertEqual(x[:], expected)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
with self.assertRaisesRegex(TypeError, "not writable"):
|
||||
(c_char * 16).from_buffer(b"a" * 16)
|
||||
with self.assertRaises(TypeError):
|
||||
with self.assertRaisesRegex(TypeError, "not writable"):
|
||||
(c_char * 16).from_buffer(memoryview(b"a" * 16))
|
||||
with self.assertRaisesRegex(TypeError, "not C contiguous"):
|
||||
(c_char * 16).from_buffer(memoryview(bytearray(b"a" * 16))[::-1])
|
||||
msg = "does not have the buffer interface"
|
||||
with self.assertRaisesRegex(TypeError, msg):
|
||||
(c_char * 16).from_buffer("a" * 16)
|
||||
|
||||
def test_fortran_contiguous(self):
|
||||
try:
|
||||
import _testbuffer
|
||||
except ImportError as err:
|
||||
self.skipTest(str(err))
|
||||
flags = _testbuffer.ND_WRITABLE | _testbuffer.ND_FORTRAN
|
||||
array = _testbuffer.ndarray(
|
||||
[97] * 16, format="B", shape=[4, 4], flags=flags)
|
||||
with self.assertRaisesRegex(TypeError, "not C contiguous"):
|
||||
(c_char * 16).from_buffer(array)
|
||||
array = memoryview(array)
|
||||
self.assertTrue(array.f_contiguous)
|
||||
self.assertFalse(array.c_contiguous)
|
||||
with self.assertRaisesRegex(TypeError, "not C contiguous"):
|
||||
(c_char * 16).from_buffer(array)
|
||||
|
||||
def test_from_buffer_with_offset(self):
|
||||
a = array.array("i", range(16))
|
||||
x = (c_int * 15).from_buffer(a, sizeof(c_int))
|
||||
|
@ -55,6 +76,12 @@ class Test(unittest.TestCase):
|
|||
with self.assertRaises(ValueError):
|
||||
(c_int * 1).from_buffer(a, 16 * sizeof(c_int))
|
||||
|
||||
def test_from_buffer_memoryview(self):
|
||||
a = [c_char.from_buffer(memoryview(bytearray(b'a')))]
|
||||
a.append(a)
|
||||
del a
|
||||
gc.collect() # Should not crash
|
||||
|
||||
def test_from_buffer_copy(self):
|
||||
a = array.array("i", range(16))
|
||||
x = (c_int * 16).from_buffer_copy(a)
|
||||
|
|
|
@ -391,6 +391,7 @@ Gökcen Eraslan
|
|||
Stoffel Erasmus
|
||||
Jürgen A. Erhard
|
||||
Michael Ernst
|
||||
Eryksun
|
||||
Ben Escoto
|
||||
Andy Eskilsson
|
||||
André Espaze
|
||||
|
|
|
@ -103,6 +103,10 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #25498: Fix a crash when garbage-collecting ctypes objects created
|
||||
by wrapping a memoryview. This was a regression made in 3.4.3. Based
|
||||
on patch by Eryksun.
|
||||
|
||||
- Issue #18010: Fix the pydoc web server's module search function to handle
|
||||
exceptions from importing packages.
|
||||
|
||||
|
|
|
@ -463,45 +463,65 @@ KeepRef(CDataObject *target, Py_ssize_t index, PyObject *keep);
|
|||
static PyObject *
|
||||
CDataType_from_buffer(PyObject *type, PyObject *args)
|
||||
{
|
||||
Py_buffer buffer;
|
||||
PyObject *obj;
|
||||
PyObject *mv;
|
||||
PyObject *result;
|
||||
Py_buffer *buffer;
|
||||
Py_ssize_t offset = 0;
|
||||
PyObject *result, *mv;
|
||||
|
||||
StgDictObject *dict = PyType_stgdict(type);
|
||||
assert (dict);
|
||||
|
||||
if (!PyArg_ParseTuple(args, "w*|n:from_buffer", &buffer, &offset))
|
||||
if (!PyArg_ParseTuple(args, "O|n:from_buffer", &obj, &offset))
|
||||
return NULL;
|
||||
|
||||
mv = PyMemoryView_FromObject(obj);
|
||||
if (mv == NULL)
|
||||
return NULL;
|
||||
|
||||
buffer = PyMemoryView_GET_BUFFER(mv);
|
||||
|
||||
if (buffer->readonly) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"underlying buffer is not writable");
|
||||
Py_DECREF(mv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!PyBuffer_IsContiguous(buffer, 'C')) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"underlying buffer is not C contiguous");
|
||||
Py_DECREF(mv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (offset < 0) {
|
||||
PyErr_SetString(PyExc_ValueError,
|
||||
"offset cannot be negative");
|
||||
PyBuffer_Release(&buffer);
|
||||
Py_DECREF(mv);
|
||||
return NULL;
|
||||
}
|
||||
if (dict->size > buffer.len - offset) {
|
||||
|
||||
if (dict->size > buffer->len - offset) {
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"Buffer size too small (%zd instead of at least %zd bytes)",
|
||||
buffer.len, dict->size + offset);
|
||||
PyBuffer_Release(&buffer);
|
||||
"Buffer size too small "
|
||||
"(%zd instead of at least %zd bytes)",
|
||||
buffer->len, dict->size + offset);
|
||||
Py_DECREF(mv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = PyCData_AtAddress(type, (char *)buffer.buf + offset);
|
||||
result = PyCData_AtAddress(type, (char *)buffer->buf + offset);
|
||||
if (result == NULL) {
|
||||
PyBuffer_Release(&buffer);
|
||||
Py_DECREF(mv);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mv = PyMemoryView_FromBuffer(&buffer);
|
||||
if (mv == NULL) {
|
||||
PyBuffer_Release(&buffer);
|
||||
if (-1 == KeepRef((CDataObject *)result, -1, mv)) {
|
||||
Py_DECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
/* Hack the memoryview so that it will release the buffer. */
|
||||
((PyMemoryViewObject *)mv)->mbuf->master.obj = buffer.obj;
|
||||
((PyMemoryViewObject *)mv)->view.obj = buffer.obj;
|
||||
if (-1 == KeepRef((CDataObject *)result, -1, mv))
|
||||
result = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue