[Python-checkins] r84649 - in python/branches/py3k: Doc/library/stdtypes.rst Lib/test/test_memoryview.py Misc/NEWS Objects/memoryobject.c

antoine.pitrou python-checkins at python.org
Thu Sep 9 14:59:39 CEST 2010


Author: antoine.pitrou
Date: Thu Sep 9 14:59:39 2010
New Revision: 84649
Log:
Issue #9757: memoryview objects get a release() method to release the
underlying buffer (previously this was only done when deallocating the
memoryview), and gain support for the context management protocol.
Modified:
 python/branches/py3k/Doc/library/stdtypes.rst
 python/branches/py3k/Lib/test/test_memoryview.py
 python/branches/py3k/Misc/NEWS
 python/branches/py3k/Objects/memoryobject.c
Modified: python/branches/py3k/Doc/library/stdtypes.rst
==============================================================================
--- python/branches/py3k/Doc/library/stdtypes.rst	(original)
+++ python/branches/py3k/Doc/library/stdtypes.rst	Thu Sep 9 14:59:39 2010
@@ -2311,7 +2311,40 @@
 
 Notice how the size of the memoryview object cannot be changed.
 
- :class:`memoryview` has two methods:
+ :class:`memoryview` has several methods:
+
+ .. method:: release()
+
+ Release the underlying buffer exposed by the memoryview object. Many
+ objects take special actions when a view is held on them (for example,
+ a :class:`bytearray` would temporarily forbid resizing); therefore,
+ calling release() is handy to remove these restrictions (and free any
+ dangling resources) as soon as possible.
+
+ After this method has been called, any further operation on the view
+ raises a :class:`ValueError` (except :meth:`release()` itself which can
+ be called multiple times)::
+
+ >>> m = memoryview(b'abc')
+ >>> m.release()
+ >>> m[0]
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ ValueError: operation forbidden on released memoryview object
+
+ The context management protocol can be used for a similar effect,
+ using the ``with`` statement::
+
+ >>> with memoryview(b'abc') as m:
+ ... m[0]
+ ...
+ b'a'
+ >>> m[0]
+ Traceback (most recent call last):
+ File "<stdin>", line 1, in <module>
+ ValueError: operation forbidden on released memoryview object
+
+ .. versionadded:: 3.2
 
 .. method:: tobytes()
 
Modified: python/branches/py3k/Lib/test/test_memoryview.py
==============================================================================
--- python/branches/py3k/Lib/test/test_memoryview.py	(original)
+++ python/branches/py3k/Lib/test/test_memoryview.py	Thu Sep 9 14:59:39 2010
@@ -225,6 +225,51 @@
 gc.collect()
 self.assertTrue(wr() is None, wr())
 
+ def _check_released(self, m, tp):
+ check = self.assertRaisesRegexp(ValueError, "released")
+ with check: bytes(m)
+ with check: m.tobytes()
+ with check: m.tolist()
+ with check: m[0]
+ with check: m[0] = b'x'
+ with check: len(m)
+ with check: m.format
+ with check: m.itemsize
+ with check: m.ndim
+ with check: m.readonly
+ with check: m.shape
+ with check: m.strides
+ with check:
+ with m:
+ pass
+ # str() and repr() still function
+ self.assertIn("released memory", str(m))
+ self.assertIn("released memory", repr(m))
+ self.assertEqual(m, m)
+ self.assertNotEqual(m, memoryview(tp(self._source)))
+ self.assertNotEqual(m, tp(self._source))
+
+ def test_contextmanager(self):
+ for tp in self._types:
+ b = tp(self._source)
+ m = self._view(b)
+ with m as cm:
+ self.assertIs(cm, m)
+ self._check_released(m, tp)
+ m = self._view(b)
+ # Can release explicitly inside the context manager
+ with m:
+ m.release()
+
+ def test_release(self):
+ for tp in self._types:
+ b = tp(self._source)
+ m = self._view(b)
+ m.release()
+ self._check_released(m, tp)
+ # Can be called a second time (it's a no-op)
+ m.release()
+ self._check_released(m, tp)
 
 # Variations on source objects for the buffer: bytes-like objects, then arrays
 # with itemsize > 1.
Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Thu Sep 9 14:59:39 2010
@@ -10,6 +10,10 @@
 Core and Builtins
 -----------------
 
+- Issue #9757: memoryview objects get a release() method to release the
+ underlying buffer (previously this was only done when deallocating the
+ memoryview), and gain support for the context management protocol.
+
 - Issue #9797: pystate.c wrongly assumed that zero couldn't be a valid
 thread-local storage key.
 
Modified: python/branches/py3k/Objects/memoryobject.c
==============================================================================
--- python/branches/py3k/Objects/memoryobject.c	(original)
+++ python/branches/py3k/Objects/memoryobject.c	Thu Sep 9 14:59:39 2010
@@ -3,6 +3,23 @@
 
 #include "Python.h"
 
+#define IS_RELEASED(memobj) \
+ (((PyMemoryViewObject *) memobj)->view.buf == NULL)
+
+#define CHECK_RELEASED(memobj) \
+ if (IS_RELEASED(memobj)) { \
+ PyErr_SetString(PyExc_ValueError, \
+ "operation forbidden on released memoryview object"); \
+ return NULL; \
+ }
+
+#define CHECK_RELEASED_INT(memobj) \
+ if (IS_RELEASED(memobj)) { \
+ PyErr_SetString(PyExc_ValueError, \
+ "operation forbidden on released memoryview object"); \
+ return -1; \
+ }
+
 static Py_ssize_t
 get_shape0(Py_buffer *buf)
 {
@@ -34,6 +51,7 @@
 memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
 {
 int res = 0;
+ CHECK_RELEASED_INT(self);
 /* XXX for whatever reason fixing the flags seems necessary */
 if (self->view.readonly)
 flags &= ~PyBUF_WRITABLE;
@@ -330,12 +348,14 @@
 static PyObject *
 memory_format_get(PyMemoryViewObject *self)
 {
+ CHECK_RELEASED(self);
 return PyUnicode_FromString(self->view.format);
 }
 
 static PyObject *
 memory_itemsize_get(PyMemoryViewObject *self)
 {
+ CHECK_RELEASED(self);
 return PyLong_FromSsize_t(self->view.itemsize);
 }
 
@@ -366,30 +386,35 @@
 static PyObject *
 memory_shape_get(PyMemoryViewObject *self)
 {
+ CHECK_RELEASED(self);
 return _IntTupleFromSsizet(self->view.ndim, self->view.shape);
 }
 
 static PyObject *
 memory_strides_get(PyMemoryViewObject *self)
 {
+ CHECK_RELEASED(self);
 return _IntTupleFromSsizet(self->view.ndim, self->view.strides);
 }
 
 static PyObject *
 memory_suboffsets_get(PyMemoryViewObject *self)
 {
+ CHECK_RELEASED(self);
 return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets);
 }
 
 static PyObject *
 memory_readonly_get(PyMemoryViewObject *self)
 {
+ CHECK_RELEASED(self);
 return PyBool_FromLong(self->view.readonly);
 }
 
 static PyObject *
 memory_ndim_get(PyMemoryViewObject *self)
 {
+ CHECK_RELEASED(self);
 return PyLong_FromLong(self->view.ndim);
 }
 
@@ -408,6 +433,7 @@
 static PyObject *
 memory_tobytes(PyMemoryViewObject *mem, PyObject *noargs)
 {
+ CHECK_RELEASED(mem);
 return PyObject_CallFunctionObjArgs(
 (PyObject *) &PyBytes_Type, mem, NULL);
 }
@@ -423,6 +449,7 @@
 PyObject *res, *item;
 char *buf;
 
+ CHECK_RELEASED(mem);
 if (strcmp(view->format, "B") || view->itemsize != 1) {
 PyErr_SetString(PyExc_NotImplementedError, 
 "tolist() only supports byte views");
@@ -449,17 +476,9 @@
 return res;
 }
 
-static PyMethodDef memory_methods[] = {
- {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL},
- {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL},
- {NULL, NULL} /* sentinel */
-};
-
-
 static void
-memory_dealloc(PyMemoryViewObject *self)
+do_release(PyMemoryViewObject *self)
 {
- _PyObject_GC_UNTRACK(self);
 if (self->view.obj != NULL) {
 if (self->base && PyTuple_Check(self->base)) {
 /* Special case when first element is generic object
@@ -484,19 +503,57 @@
 }
 Py_CLEAR(self->base);
 }
+ self->view.obj = NULL;
+ self->view.buf = NULL;
+}
+
+static PyObject *
+memory_enter(PyObject *self, PyObject *args)
+{
+ CHECK_RELEASED(self);
+ Py_INCREF(self);
+ return self;
+}
+
+static PyObject *
+memory_exit(PyObject *self, PyObject *args)
+{
+ do_release((PyMemoryViewObject *) self);
+ Py_RETURN_NONE;
+}
+
+static PyMethodDef memory_methods[] = {
+ {"release", memory_exit, METH_NOARGS},
+ {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL},
+ {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL},
+ {"__enter__", memory_enter, METH_NOARGS},
+ {"__exit__", memory_exit, METH_VARARGS},
+ {NULL, NULL} /* sentinel */
+};
+
+
+static void
+memory_dealloc(PyMemoryViewObject *self)
+{
+ _PyObject_GC_UNTRACK(self);
+ do_release(self);
 PyObject_GC_Del(self);
 }
 
 static PyObject *
 memory_repr(PyMemoryViewObject *self)
 {
- return PyUnicode_FromFormat("<memory at %p>", self);
+ if (IS_RELEASED(self))
+ return PyUnicode_FromFormat("<released memory at %p>", self);
+ else
+ return PyUnicode_FromFormat("<memory at %p>", self);
 }
 
 /* Sequence methods */
 static Py_ssize_t
 memory_length(PyMemoryViewObject *self)
 {
+ CHECK_RELEASED_INT(self);
 return get_shape0(&self->view);
 }
 
@@ -508,6 +565,7 @@
 {
 Py_buffer *view = &(self->view);
 
+ CHECK_RELEASED(self);
 if (view->ndim == 0) {
 PyErr_SetString(PyExc_IndexError,
 "invalid indexing of 0-dim memory");
@@ -557,6 +615,7 @@
 Py_buffer *view;
 view = &(self->view);
 
+ CHECK_RELEASED(self);
 if (view->ndim == 0) {
 if (key == Py_Ellipsis ||
 (PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) {
@@ -626,6 +685,7 @@
 Py_buffer *view = &(self->view);
 char *srcbuf, *destbuf;
 
+ CHECK_RELEASED_INT(self);
 if (view->readonly) {
 PyErr_SetString(PyExc_TypeError,
 "cannot modify read-only memory");
@@ -718,6 +778,11 @@
 ww.obj = NULL;
 if (op != Py_EQ && op != Py_NE)
 goto _notimpl;
+ if ((PyMemoryView_Check(v) && IS_RELEASED(v)) ||
+ (PyMemoryView_Check(w) && IS_RELEASED(w))) {
+ equal = (v == w);
+ goto _end;
+ }
 if (PyObject_GetBuffer(v, &vv, PyBUF_CONTIG_RO) == -1) {
 PyErr_Clear();
 goto _notimpl;


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /