'PickleBuffer can only be pickled with protocol >= 5')
def test_non_continuous_buffer(self):
- if self.pickler is pickle._Pickler:
- self.skipTest('CRASHES (see gh-122306)')
for proto in protocols[5:]:
with self.subTest(proto=proto):
pb = pickle.PickleBuffer(memoryview(b"foobar")[::2])
- with self.assertRaises(pickle.PicklingError):
+ with self.assertRaises((pickle.PicklingError, BufferError)):
self.dumps(pb, proto)
def test_buffer_callback_error(self):
from test.support import import_helper
+class MyObject:
+ pass
+
+
class AbstractMemoryTests:
source_bytes = b"abcdef"
self.m = memoryview(base)
class MySource(tp):
pass
- class MyObject:
- pass
# Create a reference cycle through a memoryview object.
# This exercises mbuf_clear().
m[0] = MyBool()
self.assertEqual(ba[:8], b'\0'*8)
+ def test_buffer_reference_loop(self):
+ m = memoryview(b'abc').__buffer__(0)
+ o = MyObject()
+ o.m = m
+ o.o = o
+ wr = weakref.ref(o)
+ del m, o
+ gc.collect()
+ self.assertIsNone(wr())
+
+ def test_picklebuffer_reference_loop(self):
+ pb = pickle.PickleBuffer(memoryview(b'abc'))
+ o = MyObject()
+ o.pb = pb
+ o.o = o
+ wr = weakref.ref(o)
+ del pb, o
+ gc.collect()
+ self.assertIsNone(wr())
+
+
if __name__ == "__main__":
unittest.main()
if (self->flags&_Py_MANAGED_BUFFER_RELEASED)
return;
- /* NOTE: at this point self->exports can still be > 0 if this function
- is called from mbuf_clear() to break up a reference cycle. */
self->flags |= _Py_MANAGED_BUFFER_RELEASED;
/* PyBuffer_Release() decrements master->obj and sets it to NULL. */
/* Inform the managed buffer that this particular memoryview will not access
the underlying buffer again. If no other memoryviews are registered with
the managed buffer, the underlying buffer is released instantly and
- marked as inaccessible for both the memoryview and the managed buffer.
-
- This function fails if the memoryview itself has exported buffers. */
-static int
+ marked as inaccessible for both the memoryview and the managed buffer. */
+static void
_memory_release(PyMemoryViewObject *self)
{
+ assert(self->exports == 0);
if (self->flags & _Py_MEMORYVIEW_RELEASED)
- return 0;
+ return;
- if (self->exports == 0) {
- self->flags |= _Py_MEMORYVIEW_RELEASED;
- assert(self->mbuf->exports > 0);
- if (--self->mbuf->exports == 0)
- mbuf_release(self->mbuf);
- return 0;
+ self->flags |= _Py_MEMORYVIEW_RELEASED;
+ assert(self->mbuf->exports > 0);
+ if (--self->mbuf->exports == 0) {
+ mbuf_release(self->mbuf);
}
- if (self->exports > 0) {
- PyErr_Format(PyExc_BufferError,
- "memoryview has %zd exported buffer%s", self->exports,
- self->exports==1 ? "" : "s");
- return -1;
- }
-
- PyErr_SetString(PyExc_SystemError,
- "_memory_release(): negative export count");
- return -1;
}
/*[clinic input]
memoryview_release_impl(PyMemoryViewObject *self)
/*[clinic end generated code: output=d0b7e3ba95b7fcb9 input=bc71d1d51f4a52f0]*/
{
- if (_memory_release(self) < 0)
+ if (self->exports == 0) {
+ _memory_release(self);
+ Py_RETURN_NONE;
+ }
+
+ if (self->exports > 0) {
+ PyErr_Format(PyExc_BufferError,
+ "memoryview has %zd exported buffer%s", self->exports,
+ self->exports==1 ? "" : "s");
return NULL;
- Py_RETURN_NONE;
+ }
+
+ PyErr_SetString(PyExc_SystemError,
+ "memoryview: negative export count");
+ return NULL;
}
static void
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
assert(self->exports == 0);
_PyObject_GC_UNTRACK(self);
- (void)_memory_release(self);
+ _memory_release(self);
Py_CLEAR(self->mbuf);
if (self->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) self);
memory_clear(PyObject *_self)
{
PyMemoryViewObject *self = (PyMemoryViewObject *)_self;
- (void)_memory_release(self);
- Py_CLEAR(self->mbuf);
+ if (self->exports == 0) {
+ _memory_release(self);
+ Py_CLEAR(self->mbuf);
+ }
return 0;
}