diff options
author | Pauli Virtanen <pav@iki.fi> | 2009-12-06 12:04:47 +0000 |
---|---|---|
committer | Pauli Virtanen <pav@iki.fi> | 2009-12-06 12:04:47 +0000 |
commit | 816035e8881f19437b7f1d08b30e48cf4adac278 (patch) | |
tree | abc6b9b5442ab55c863e7ce60476836b8a74de79 /numpy/core/src | |
parent | 795b044cc36fbe63b681147767cf18e28ecf2b51 (diff) | |
download | numpy-816035e8881f19437b7f1d08b30e48cf4adac278.tar.gz |
core: Implement the PEP 3118 buffer protocol (exporter part)
Also, use MemoryView instead of PyBuffer for Py3.
Diffstat (limited to 'numpy/core/src')
-rw-r--r-- | numpy/core/src/multiarray/arraytypes.c.src | 12 | ||||
-rw-r--r-- | numpy/core/src/multiarray/buffer.c | 275 | ||||
-rw-r--r-- | numpy/core/src/multiarray/common.c | 4 | ||||
-rw-r--r-- | numpy/core/src/multiarray/conversion_utils.c | 6 | ||||
-rw-r--r-- | numpy/core/src/multiarray/ctors.c | 25 | ||||
-rw-r--r-- | numpy/core/src/multiarray/descriptor.c | 6 | ||||
-rw-r--r-- | numpy/core/src/multiarray/getset.c | 12 | ||||
-rw-r--r-- | numpy/core/src/multiarray/multiarraymodule.c | 29 | ||||
-rw-r--r-- | numpy/core/src/multiarray/scalartypes.c.src | 68 |
9 files changed, 421 insertions, 16 deletions
diff --git a/numpy/core/src/multiarray/arraytypes.c.src b/numpy/core/src/multiarray/arraytypes.c.src index 5971dd081..15a7cb86c 100644 --- a/numpy/core/src/multiarray/arraytypes.c.src +++ b/numpy/core/src/multiarray/arraytypes.c.src @@ -586,10 +586,22 @@ finish: } itemsize = ap->descr->elsize; if (PyArray_ISWRITEABLE(ap)) { +#if defined(NPY_PY3K) +#warning XXX -- needs implementation + PyErr_SetString(PyExc_RuntimeError, "XXX -- not implemented"); + u = NULL; +#else u = PyBuffer_FromReadWriteMemory(ip, itemsize); +#endif } else { +#if defined(NPY_PY3K) +#warning XXX -- needs implementation + PyErr_SetString(PyExc_RuntimeError, "XXX -- not implemented"); + u = NULL; +#else u = PyBuffer_FromMemory(ip, itemsize); +#endif } if (u == NULL) { goto fail; diff --git a/numpy/core/src/multiarray/buffer.c b/numpy/core/src/multiarray/buffer.c index 5cf5054bd..f94de4364 100644 --- a/numpy/core/src/multiarray/buffer.c +++ b/numpy/core/src/multiarray/buffer.c @@ -69,20 +69,287 @@ array_getcharbuf(PyArrayObject *self, Py_ssize_t segment, constchar **ptrptr) return array_getreadbuf(self, segment, (void **) ptrptr); } + +#if PY_VERSION_HEX >= 0x02060000 + +/* + * Buffer protocol format string translator + */ + +typedef struct { + char *s; + int allocated; + int pos; +} _tmp_string; + +static int +_append_char(_tmp_string *s, char c) +{ + char *p; + if (s->s == NULL) { + s->s = (char*)malloc(16); + s->pos = 0; + s->allocated = 16; + } + if (s->pos >= s->allocated) { + p = (char*)realloc(s->s, 2*s->allocated); + if (p == NULL) { + PyErr_SetString(PyExc_MemoryError, "memory allocation failed"); + return -1; + } + s->s = p; + s->allocated *= 2; + } + s->s[s->pos] = c; + ++s->pos; + return 0; +} + +static int +_append_str(_tmp_string *s, char *c) +{ + while (*c != '\0') { + if (_append_char(s, *c)) return -1; + ++c; + } +} + +static int +_buffer_format_string(PyArray_Descr *descr, _tmp_string *str, int *offset) +{ + PyObject *s; + int k; + int zero_offset = 0; + + if (descr->subarray) { + PyErr_SetString(PyExc_ValueError, + "data types with sub-arrays cannot be exported as " + "buffers"); + return -1; + } + else if (PyDataType_HASFIELDS(descr)) { + _append_str(str, "T{"); + for (k = 0; k < PyTuple_GET_SIZE(descr->names); ++k) { + PyObject *name, *item, *offset_obj, *tmp; + PyArray_Descr *child; + char *p; + Py_ssize_t len; + int new_offset; + + name = PyTuple_GET_ITEM(descr->names, k); + item = PyDict_GetItem(descr->fields, name); + + child = (PyArray_Descr*)PyTuple_GetItem(item, 0); + offset_obj = PyTuple_GetItem(item, 1); + new_offset = PyInt_AsLong(offset_obj); + + /* Insert padding manually */ + while (*offset < new_offset) { + _append_char(str, 'x'); + ++*offset; + } + *offset += child->elsize; + + /* Insert child item */ + _buffer_format_string(child, str, offset); + + /* Insert field name */ +#if defined(NPY_PY3K) +#warning XXX -- should it use UTF-8 here? + tmp = PyUnicode_AsUTF8String(name); +#else + tmp = name; +#endif + if (tmp == NULL || PyBytes_AsStringAndSize(tmp, &p, &len) < 0) { + PyErr_SetString(PyExc_ValueError, "invalid field name"); + return -1; + } + _append_char(str, ':'); + while (len > 0) { + if (*p == ':') { + Py_DECREF(tmp); + PyErr_SetString(PyExc_ValueError, + "':' is not an allowed character in buffer " + "field names"); + return -1; + } + _append_char(str, *p); + ++p; + --len; + } + _append_char(str, ':'); +#if defined(NPY_PY3K) + Py_DECREF(tmp); +#endif + } + _append_char(str, '}'); + } + else { + if (descr->byteorder == '<' || descr->byteorder == '>' || + descr->byteorder == '=') { + _append_char(str, descr->byteorder); + } + + switch (descr->type_num) { + case NPY_BYTE: if (_append_char(str, 'b')) return -1; break; + case NPY_UBYTE: if (_append_char(str, 'B')) return -1; break; + case NPY_SHORT: if (_append_char(str, 'h')) return -1; break; + case NPY_USHORT: if (_append_char(str, 'H')) return -1; break; + case NPY_INT: if (_append_char(str, 'i')) return -1; break; + case NPY_UINT: if (_append_char(str, 'I')) return -1; break; + case NPY_LONG: if (_append_char(str, 'l')) return -1; break; + case NPY_ULONG: if (_append_char(str, 'L')) return -1; break; + case NPY_LONGLONG: if (_append_char(str, 'q')) return -1; break; + case NPY_ULONGLONG: if (_append_char(str, 'Q')) return -1; break; + case NPY_FLOAT: if (_append_char(str, 'f')) return -1; break; + case NPY_DOUBLE: if (_append_char(str, 'd')) return -1; break; + case NPY_LONGDOUBLE: if (_append_char(str, 'g')) return -1; break; + case NPY_CFLOAT: if (_append_str(str, "Zf")) return -1; break; + case NPY_CDOUBLE: if (_append_str(str, "Zd")) return -1; break; + case NPY_CLONGDOUBLE: if (_append_str(str, "Zg")) return -1; break; + case NPY_STRING: { + char buf[128]; + PyOS_snprintf(buf, sizeof(buf), "%ds", descr->elsize); + if (_append_str(str, buf)) return -1; + break; + } + case NPY_UNICODE: { + /* Numpy Unicode is always 4-byte */ + char buf[128]; + assert(descr->elsize % 4 == 0); + PyOS_snprintf(buf, sizeof(buf), "%dw", descr->elsize / 4); + if (_append_str(str, buf)) return -1; + break; + } + case NPY_OBJECT: if (_append_char(str, 'O')) return -1; break; + default: + PyErr_Format(PyExc_ValueError, "unknown dtype code %d", + descr->type_num); + return -1; + } + } + + return 0; +} + +/* + * The new buffer protocol + */ + +static int +array_getbuffer(PyObject *self, Py_buffer *view, int flags) +{ + view->format = NULL; + view->shape = NULL; + + if ((flags & PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS && + !PyArray_CHKFLAGS(self, NPY_C_CONTIGUOUS)) { + PyErr_SetString(PyExc_ValueError, "ndarray is not C contiguous"); + goto fail; + } + if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS && + !PyArray_CHKFLAGS(self, NPY_F_CONTIGUOUS)) { + PyErr_SetString(PyExc_ValueError, "ndarray is not Fortran contiguous"); + goto fail; + } + if ((flags & PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS + && !PyArray_ISONESEGMENT(self)) { + PyErr_SetString(PyExc_ValueError, "ndarray is not contiguous"); + goto fail; + } + + view->buf = PyArray_DATA(self); + view->suboffsets = NULL; + view->itemsize = PyArray_ITEMSIZE(self); + view->readonly = !PyArray_ISWRITEABLE(self); + view->internal = NULL; + view->len = PyArray_NBYTES(self); + + if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) { + if (cache->format == NULL) { + int offset = 0; + _tmp_string fmt = {0,0,0}; + if (_buffer_format_string(PyArray_DESCR(self), &fmt, &offset)) { + goto fail; + } + _append_char(&fmt, '\0'); + cache->format = fmt.s; + } + _append_char(&fmt, '\0'); + view->format = fmt.s; + } + else { + view->format = NULL; + } + + if ((flags & PyBUF_STRIDED) == PyBUF_STRIDED) { + int k; + view->ndim = PyArray_NDIM(self); + view->shape = (Py_ssize_t*)malloc(sizeof(Py_ssize_t) * view->ndim * 2); + view->strides = view->shape + view->ndim; + for (k = 0; k < PyArray_NDIM(self); ++k) { + view->shape[k] = PyArray_DIMS(self)[k]; + view->strides[k] = PyArray_STRIDES(self)[k]; + } + } + else if (PyArray_ISONESEGMENT(self)) { +#warning XXX -- should try harder here to determine single-segmentness? + view->ndim = 0; + view->shape = NULL; + view->strides = NULL; + } + else { + PyErr_SetString(PyExc_ValueError, "ndarray is not single-segment"); + goto fail; + } + + view->obj = self; + Py_INCREF(self); + + return 0; + +fail: + if (view->format) { + free(view->format); + } + if (view->shape) { + free(view->shape); + } + return -1; +} + +static void +array_releasebuffer(PyObject *self, Py_buffer *view) +{ + if (view->format != NULL) { + free(view->format); + view->format = NULL; + } + if (view->shape != NULL) { + free(view->shape); + view->shape = NULL; + } +} + +#endif + + NPY_NO_EXPORT PyBufferProcs array_as_buffer = { +#if !defined(NPY_PY3K) #if PY_VERSION_HEX >= 0x02050000 (readbufferproc)array_getreadbuf, /*bf_getreadbuffer*/ (writebufferproc)array_getwritebuf, /*bf_getwritebuffer*/ (segcountproc)array_getsegcount, /*bf_getsegcount*/ (charbufferproc)array_getcharbuf, /*bf_getcharbuffer*/ -#if PY_VERSION_HEX >= 0x02060000 - (getbufferproc)0, - (releasebufferproc)0, -#endif #else (getreadbufferproc)array_getreadbuf, /*bf_getreadbuffer*/ (getwritebufferproc)array_getwritebuf, /*bf_getwritebuffer*/ (getsegcountproc)array_getsegcount, /*bf_getsegcount*/ (getcharbufferproc)array_getcharbuf, /*bf_getcharbuffer*/ #endif +#endif +#if PY_VERSION_HEX >= 0x02060000 + (getbufferproc)array_getbuffer, + (releasebufferproc)array_releasebuffer, +#endif }; diff --git a/numpy/core/src/multiarray/common.c b/numpy/core/src/multiarray/common.c index 7eec15396..f76e61460 100644 --- a/numpy/core/src/multiarray/common.c +++ b/numpy/core/src/multiarray/common.c @@ -243,7 +243,11 @@ _array_find_type(PyObject *op, PyArray_Descr *minitype, int max) goto finish; } +#if defined(NPY_PY3K) + if (PyMemoryView_Check(op)) { +#else if (PyBuffer_Check(op)) { +#endif chktype = PyArray_DescrNewFromType(PyArray_VOID); chktype->elsize = Py_TYPE(op)->tp_as_sequence->sq_length(op); PyErr_Clear(); diff --git a/numpy/core/src/multiarray/conversion_utils.c b/numpy/core/src/multiarray/conversion_utils.c index 2d507e316..b6b72464e 100644 --- a/numpy/core/src/multiarray/conversion_utils.c +++ b/numpy/core/src/multiarray/conversion_utils.c @@ -158,9 +158,15 @@ PyArray_BufferConverter(PyObject *obj, PyArray_Chunk *buf) buf->len = (intp) buflen; /* Point to the base of the buffer object if present */ +#if defined(NPY_PY3K) + if (PyMemoryView_Check(obj)) { + buf->base = PyMemoryView_GET_BASE(obj); + } +#else if (PyBuffer_Check(obj)) { buf->base = ((PyArray_Chunk *)obj)->base; } +#endif if (buf->base == NULL) { buf->base = obj; } diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 075662599..80f010e8f 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -1103,7 +1103,13 @@ discover_depth(PyObject *s, int max, int stop_at_string, int stop_at_tuple) if (PyArray_IsScalar(s, Generic)) { return 0; } - if (PyString_Check(s) || PyBuffer_Check(s) || PyUnicode_Check(s)) { + if (PyString_Check(s) || +#if defined(NPY_PY3K) + PyMemoryView_Check(s) || +#else + PyBuffer_Check(s) || +#endif + PyUnicode_Check(s)) { return stop_at_string ? 0:1; } if (stop_at_tuple && PyTuple_Check(s)) { @@ -1171,7 +1177,13 @@ discover_itemsize(PyObject *s, int nd, int *itemsize) n = PyObject_Length(s); if ((nd == 0) || PyString_Check(s) || - PyUnicode_Check(s) || PyBuffer_Check(s)) { +#if defined(NPY_PY3K) + PyMemoryView_Check(s) || +#else + PyBuffer_Check(s) || +#endif + PyUnicode_Check(s)) { + *itemsize = MAX(*itemsize, n); return 0; } @@ -3071,8 +3083,13 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, return NULL; } if (Py_TYPE(buf)->tp_as_buffer == NULL +#if defined(NPY_PY3K) + || Py_TYPE(buf)->tp_as_buffer->bf_getbuffer == NULL +#else || (Py_TYPE(buf)->tp_as_buffer->bf_getwritebuffer == NULL - && Py_TYPE(buf)->tp_as_buffer->bf_getreadbuffer == NULL)) { + && Py_TYPE(buf)->tp_as_buffer->bf_getreadbuffer == NULL) +#endif + ) { PyObject *newbuf; newbuf = PyObject_GetAttrString(buf, "__buffer__"); if (newbuf == NULL) { @@ -3085,6 +3102,8 @@ PyArray_FromBuffer(PyObject *buf, PyArray_Descr *type, Py_INCREF(buf); } +#warning XXX: Should implement support for the new buffer interface here! + if (PyObject_AsWriteBuffer(buf, (void *)&data, &ts) == -1) { write = 0; PyErr_Clear(); diff --git a/numpy/core/src/multiarray/descriptor.c b/numpy/core/src/multiarray/descriptor.c index 77d998f2b..b9fbd4175 100644 --- a/numpy/core/src/multiarray/descriptor.c +++ b/numpy/core/src/multiarray/descriptor.c @@ -1149,9 +1149,15 @@ PyArray_DescrConverter(PyObject *obj, PyArray_Descr **at) else if (obj == (PyObject *)(&PyUnicode_Type)) { check_num = PyArray_UNICODE; } +#if defined(NPY_PY3K) + else if (obj == (PyObject *)(&PyMemoryView_Type)) { + check_num = PyArray_VOID; + } +#else else if (obj == (PyObject *)(&PyBuffer_Type)) { check_num = PyArray_VOID; } +#endif else { *at = _arraydescr_fromobj(obj); if (*at) { diff --git a/numpy/core/src/multiarray/getset.c b/numpy/core/src/multiarray/getset.c index b0364d2d2..3f397a557 100644 --- a/numpy/core/src/multiarray/getset.c +++ b/numpy/core/src/multiarray/getset.c @@ -279,6 +279,9 @@ array_interface_get(PyArrayObject *self) static PyObject * array_data_get(PyArrayObject *self) { +#if defined(NPY_PY3K) + return PyMemoryView_FromObject(self); +#else intp nbytes; if (!(PyArray_ISONESEGMENT(self))) { PyErr_SetString(PyExc_AttributeError, "cannot get single-"\ @@ -286,17 +289,23 @@ array_data_get(PyArrayObject *self) return NULL; } nbytes = PyArray_NBYTES(self); - if PyArray_ISWRITEABLE(self) { + if (PyArray_ISWRITEABLE(self)) { return PyBuffer_FromReadWriteObject((PyObject *)self, 0, (Py_ssize_t) nbytes); } else { return PyBuffer_FromObject((PyObject *)self, 0, (Py_ssize_t) nbytes); } +#endif } static int array_data_set(PyArrayObject *self, PyObject *op) { +#if defined(NPY_PY3K) +#warning XXX -- need to implement this + PyErr_SetString(PyExc_RuntimeError, "XXX -- not implemented"); + return -1; +#else void *buf; Py_ssize_t buf_len; int writeable=1; @@ -338,6 +347,7 @@ array_data_set(PyArrayObject *self, PyObject *op) self->flags &= ~WRITEABLE; } return 0; +#endif } diff --git a/numpy/core/src/multiarray/multiarraymodule.c b/numpy/core/src/multiarray/multiarraymodule.c index 95d233538..430058e37 100644 --- a/numpy/core/src/multiarray/multiarraymodule.c +++ b/numpy/core/src/multiarray/multiarraymodule.c @@ -2138,14 +2138,25 @@ new_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args) if(!PyArg_ParseTuple(args, "i", &size)) { return NULL; } +#if defined(NPY_PY3K) +#warning XXX -- needs to implement this? + PyErr_SetString(PyExc_RuntimeError, "XXX -- not implemented"); + return NULL; +#else return PyBuffer_New(size); +#endif } static PyObject * buffer_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) { PyObject *obj; - Py_ssize_t offset = 0, size = Py_END_OF_BUFFER, n; + Py_ssize_t offset = 0, n; +#if defined(NPY_PY3K) + Py_ssize_t size; +#else + Py_ssize_t size = Py_END_OF_BUFFER; +#endif void *unused; static char *kwlist[] = {"object", "offset", "size", NULL}; @@ -2156,10 +2167,20 @@ buffer_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) } if (PyObject_AsWriteBuffer(obj, &unused, &n) < 0) { PyErr_Clear(); +#if defined(NPY_PY3K) +#warning XXX: should use the full memoryview capabilities + return PyMemoryView_FromObject(obj); +#else return PyBuffer_FromObject(obj, offset, size); +#endif } else { +#if defined(NPY_PY3K) +#warning XXX: should use the full memoryview capabilities + return PyMemoryView_FromObject(obj); +#else return PyBuffer_FromReadWriteObject(obj, offset, size); +#endif } } @@ -2242,10 +2263,16 @@ as_buffer(PyObject *NPY_UNUSED(dummy), PyObject *args, PyObject *kwds) } +#if defined(NPY_PY3K) + PyErr_SetString(PyExc_RuntimeError, + "XXX -- not implemented!") + return NULL; +#else if (ro) { return PyBuffer_FromMemory(memptr, size); } return PyBuffer_FromReadWriteMemory(memptr, size); +#endif } #undef _test_code diff --git a/numpy/core/src/multiarray/scalartypes.c.src b/numpy/core/src/multiarray/scalartypes.c.src index 55b615989..e51c29ab9 100644 --- a/numpy/core/src/multiarray/scalartypes.c.src +++ b/numpy/core/src/multiarray/scalartypes.c.src @@ -772,7 +772,11 @@ voidtype_dtypedescr_get(PyVoidScalarObject *self) static PyObject * gentype_data_get(PyObject *self) { +#if defined(NPY_PY3K) + return PyMemoryView_FromObject(self); +#else return PyBuffer_FromObject(self, 0, Py_END_OF_BUFFER); +#endif } @@ -1812,7 +1816,6 @@ static PySequenceMethods voidtype_as_sequence = { }; - static Py_ssize_t gentype_getreadbuf(PyObject *self, Py_ssize_t segment, void **ptrptr) { @@ -1871,15 +1874,34 @@ gentype_getcharbuf(PyObject *self, Py_ssize_t segment, constchar **ptrptr) } } +#if PY_VERSION_HEX >= 0x02060000 + +static int +gentype_getbuffer(PyObject *self, Py_buffer *view, int flags) +{ + Py_ssize_t len; + void *buf; + +#warning XXX: the format is not implemented! -- this needs more work + + len = gentype_getreadbuf(self, 0, &buf); + return PyBuffer_FillInfo(view, self, buf, len, 1, flags); +} + +/* releasebuffer is not needed */ + +#endif static PyBufferProcs gentype_as_buffer = { +#if !defined(NPY_PY3K) gentype_getreadbuf, /* bf_getreadbuffer*/ NULL, /* bf_getwritebuffer*/ gentype_getsegcount, /* bf_getsegcount*/ gentype_getcharbuf, /* bf_getcharbuffer*/ +#endif #if PY_VERSION_HEX >= 0x02060000 - NULL, - NULL, + gentype_getbuffer, /* bf_getbuffer */ + NULL, /* bf_releasebuffer */ #endif }; @@ -2635,6 +2657,7 @@ static PyMappingMethods object_arrtype_as_mapping = { #endif }; +#if !defined(NPY_PY3K) static Py_ssize_t object_arrtype_getsegcount(PyObjectScalarObject *self, Py_ssize_t *lenp) { @@ -2698,23 +2721,54 @@ object_arrtype_getcharbuf(PyObjectScalarObject *self, Py_ssize_t segment, } return (*pb->bf_getcharbuffer)(self->obval, segment, ptrptr); } +#endif + +#if PY_VERSION_HEX >= 0x02060000 +static int +object_arrtype_getbuffer(PyObjectScalarObject *self, Py_buffer *view, int flags) +{ + PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer; + if (pb == NULL || pb->bf_getbuffer == NULL) { + PyErr_SetString(PyExc_TypeError, + "expected a readable buffer object"); + return -1; + } + return (*pb->bf_getbuffer)(self->obval, view, flags); +} + +static void +object_arrtype_releasebuffer(PyObjectScalarObject *self, Py_buffer *view) +{ + PyBufferProcs *pb = Py_TYPE(self->obval)->tp_as_buffer; + if (pb == NULL) { + PyErr_SetString(PyExc_TypeError, + "expected a readable buffer object"); + return; + } + if (pb->bf_releasebuffer != NULL) { + (*pb->bf_releasebuffer)(self->obval, view); + } +} +#endif static PyBufferProcs object_arrtype_as_buffer = { +#if !defined(NPY_PY3K) #if PY_VERSION_HEX >= 0x02050000 (readbufferproc)object_arrtype_getreadbuf, (writebufferproc)object_arrtype_getwritebuf, (segcountproc)object_arrtype_getsegcount, (charbufferproc)object_arrtype_getcharbuf, -#if PY_VERSION_HEX >= 0x02060000 - (getbufferproc)0, - (releasebufferproc)0, -#endif #else (getreadbufferproc)object_arrtype_getreadbuf, (getwritebufferproc)object_arrtype_getwritebuf, (getsegcountproc)object_arrtype_getsegcount, (getcharbufferproc)object_arrtype_getcharbuf, #endif +#endif +#if PY_VERSION_HEX >= 0x02060000 + (getbufferproc)object_arrtype_getbuffer, + (releasebufferproc)object_arrtype_releasebuffer, +#endif }; static PyObject * |