c.clear()
self.assertIs(c.buffer, None)
+ def test_release_buffer_with_exception_set(self):
+ class A:
+ def __buffer__(self, flags):
+ return memoryview(bytes(8))
+ def __release_buffer__(self, view):
+ pass
+
+ b = bytearray(8)
+ with memoryview(b):
+ # now b.extend will raise an exception due to exports
+ with self.assertRaises(BufferError):
+ b.extend(A())
+
if __name__ == "__main__":
unittest.main()
static void
releasebuffer_call_python(PyObject *self, Py_buffer *buffer)
{
+ // bf_releasebuffer may be called while an exception is already active.
+ // We have no way to report additional errors up the stack, because
+ // this slot returns void, so we simply stash away the active exception
+ // and restore it after the call to Python returns.
+ PyObject *exc = PyErr_GetRaisedException();
+
PyObject *mv;
bool is_buffer_wrapper = Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type;
if (is_buffer_wrapper) {
// __release_buffer__() that __buffer__() returned.
PyBufferWrapper *bw = (PyBufferWrapper *)buffer->obj;
if (bw->mv == NULL) {
- return;
+ goto end;
}
mv = Py_NewRef(bw->mv);
}
mv = PyMemoryView_FromBuffer(buffer);
if (mv == NULL) {
PyErr_WriteUnraisable(self);
- return;
+ goto end;
}
// Set the memoryview to restricted mode, which forbids
// users from saving any reference to the underlying buffer
PyObject_CallMethodNoArgs(mv, &_Py_ID(release));
}
Py_DECREF(mv);
+end:
+ assert(!PyErr_Occurred());
+
+ PyErr_SetRaisedException(exc);
}
/*