[pypy-commit] pypy default: merge memoryview-attributes
plan_rich
pypy.commits at gmail.com
Wed Aug 24 03:14:20 EDT 2016
Author: Richard Plangger <planrichi at gmail.com>
Branch:
Changeset: r86456:0d29b975452d
Date: 2016-08-24 09:10 +0200
http://bitbucket.org/pypy/pypy/changeset/0d29b975452d/
Log: merge memoryview-attributes
diff --git a/lib_pypy/_ctypes/basics.py b/lib_pypy/_ctypes/basics.py
--- a/lib_pypy/_ctypes/basics.py
+++ b/lib_pypy/_ctypes/basics.py
@@ -167,7 +167,7 @@
else:
return self.value
- def __buffer__(self):
+ def __buffer__(self, flags):
return buffer(self._buffer)
def _get_b_base(self):
diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py
--- a/pypy/interpreter/baseobjspace.py
+++ b/pypy/interpreter/baseobjspace.py
@@ -208,7 +208,8 @@
def buffer_w(self, space, flags):
w_impl = space.lookup(self, '__buffer__')
if w_impl is not None:
- w_result = space.get_and_call_function(w_impl, self)
+ w_result = space.get_and_call_function(w_impl, self,
+ space.newint(flags))
if space.isinstance_w(w_result, space.w_buffer):
return w_result.buffer_w(space, flags)
raise BufferInterfaceNotFound
@@ -216,7 +217,8 @@
def readbuf_w(self, space):
w_impl = space.lookup(self, '__buffer__')
if w_impl is not None:
- w_result = space.get_and_call_function(w_impl, self)
+ w_result = space.get_and_call_function(w_impl, self,
+ space.newint(space.BUF_FULL_RO))
if space.isinstance_w(w_result, space.w_buffer):
return w_result.readbuf_w(space)
raise BufferInterfaceNotFound
@@ -224,7 +226,8 @@
def writebuf_w(self, space):
w_impl = space.lookup(self, '__buffer__')
if w_impl is not None:
- w_result = space.get_and_call_function(w_impl, self)
+ w_result = space.get_and_call_function(w_impl, self,
+ space.newint(space.BUF_FULL))
if space.isinstance_w(w_result, space.w_buffer):
return w_result.writebuf_w(space)
raise BufferInterfaceNotFound
@@ -232,7 +235,8 @@
def charbuf_w(self, space):
w_impl = space.lookup(self, '__buffer__')
if w_impl is not None:
- w_result = space.get_and_call_function(w_impl, self)
+ w_result = space.get_and_call_function(w_impl, self,
+ space.newint(space.BUF_FULL_RO))
if space.isinstance_w(w_result, space.w_buffer):
return w_result.charbuf_w(space)
raise BufferInterfaceNotFound
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -121,7 +121,7 @@
Py_TPFLAGS_READY Py_TPFLAGS_READYING Py_TPFLAGS_HAVE_GETCHARBUFFER
METH_COEXIST METH_STATIC METH_CLASS Py_TPFLAGS_BASETYPE
METH_NOARGS METH_VARARGS METH_KEYWORDS METH_O
-Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS
+Py_TPFLAGS_HEAPTYPE Py_TPFLAGS_HAVE_CLASS Py_TPFLAGS_HAVE_NEWBUFFER
Py_LT Py_LE Py_EQ Py_NE Py_GT Py_GE Py_TPFLAGS_CHECKTYPES
""".split()
for name in constant_names:
@@ -649,6 +649,7 @@
#('smalltable', rffi.CFixedArray(Py_ssize_t, 2)),
('internal', rffi.VOIDP)
))
+Py_bufferP = lltype.Ptr(Py_buffer)
@specialize.memo()
def is_PyObject(TYPE):
diff --git a/pypy/module/cpyext/buffer.py b/pypy/module/cpyext/buffer.py
--- a/pypy/module/cpyext/buffer.py
+++ b/pypy/module/cpyext/buffer.py
@@ -1,13 +1,17 @@
from pypy.interpreter.error import oefmt
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.module.cpyext.api import (
- cpython_api, CANNOT_FAIL, Py_buffer)
+ cpython_api, CANNOT_FAIL, Py_buffer, Py_TPFLAGS_HAVE_NEWBUFFER)
from pypy.module.cpyext.pyobject import PyObject
@cpython_api([PyObject], rffi.INT_real, error=CANNOT_FAIL)
-def PyObject_CheckBuffer(space, w_obj):
+def PyObject_CheckBuffer(space, pyobj):
"""Return 1 if obj supports the buffer interface otherwise 0."""
- return 0 # the bf_getbuffer field is never filled by cpyext
+ as_buffer = pyobj.c_ob_type.c_tp_as_buffer
+ flags = pyobj.c_ob_type.c_tp_flags
+ if (flags & Py_TPFLAGS_HAVE_NEWBUFFER and as_buffer.c_bf_getbuffer):
+ return 1
+ return 0
@cpython_api([PyObject, lltype.Ptr(Py_buffer), rffi.INT_real],
rffi.INT_real, error=-1)
diff --git a/pypy/module/cpyext/memoryobject.py b/pypy/module/cpyext/memoryobject.py
--- a/pypy/module/cpyext/memoryobject.py
+++ b/pypy/module/cpyext/memoryobject.py
@@ -12,7 +12,7 @@
@cpython_api([PyObject], PyObject)
def PyMemoryView_GET_BASE(space, w_obj):
# return the obj field of the Py_buffer created by PyMemoryView_GET_BUFFER
- raise NotImplementedError
+ raise NotImplementedError('PyMemoryView_GET_BUFFER')
@cpython_api([PyObject], lltype.Ptr(Py_buffer), error=CANNOT_FAIL)
def PyMemoryView_GET_BUFFER(space, w_obj):
diff --git a/pypy/module/cpyext/slotdefs.py b/pypy/module/cpyext/slotdefs.py
--- a/pypy/module/cpyext/slotdefs.py
+++ b/pypy/module/cpyext/slotdefs.py
@@ -3,15 +3,16 @@
import re
from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rlib.rarithmetic import widen
from pypy.module.cpyext.api import (
cpython_api, generic_cpy_call, PyObject, Py_ssize_t, Py_TPFLAGS_CHECKTYPES,
- mangle_name, pypy_decl)
+ mangle_name, pypy_decl, Py_buffer, Py_bufferP)
from pypy.module.cpyext.typeobjectdefs import (
unaryfunc, wrapperfunc, ternaryfunc, PyTypeObjectPtr, binaryfunc, ternaryfunc,
getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry,
ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc,
cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc,
- readbufferproc, ssizessizeobjargproc)
+ readbufferproc, getbufferproc, ssizessizeobjargproc)
from pypy.module.cpyext.pyobject import from_ref, make_ref, Py_DecRef
from pypy.module.cpyext.pyerrors import PyErr_Occurred
from pypy.module.cpyext.state import State
@@ -22,6 +23,9 @@
from rpython.rlib.objectmodel import specialize
from rpython.tool.sourcetools import func_renamer
from rpython.rtyper.annlowlevel import llhelper
+from pypy.module.sys.version import CPYTHON_VERSION
+
+PY3 = CPYTHON_VERSION[0] == 3
# XXX: Also defined in object.h
Py_LT = 0
@@ -298,11 +302,23 @@
# Similar to Py_buffer
_immutable_ = True
- def __init__(self, ptr, size, w_obj):
+ def __init__(self, ptr, size, w_obj, format='B', shape=None,
+ strides=None, ndim=1, itemsize=1, readonly=True):
self.ptr = ptr
self.size = size
self.w_obj = w_obj # kept alive
- self.readonly = True
+ self.format = format
+ if not shape:
+ self.shape = [size]
+ else:
+ self.shape = shape
+ if not strides:
+ self.strides = [1]
+ else:
+ self.strides = strides
+ self.ndim = ndim
+ self.itemsize = itemsize
+ self.readonly = readonly
def getlength(self):
return self.size
@@ -313,6 +329,15 @@
def get_raw_address(self):
return rffi.cast(rffi.CCHARP, self.ptr)
+ def getformat(self):
+ return self.format
+
+ def getshape(self):
+ return self.shape
+
+ def getitemsize(self):
+ return self.itemsize
+
def wrap_getreadbuffer(space, w_self, w_args, func):
func_target = rffi.cast(readbufferproc, func)
with lltype.scoped_alloc(rffi.VOIDPP.TO, 1) as ptr:
@@ -322,6 +347,30 @@
space.fromcache(State).check_and_raise_exception(always=True)
return space.newbuffer(CPyBuffer(ptr[0], size, w_self))
+def wrap_getbuffer(space, w_self, w_args, func):
+ func_target = rffi.cast(getbufferproc, func)
+ with lltype.scoped_alloc(Py_buffer) as pybuf:
+ _flags = 0
+ if space.len_w(w_args) > 0:
+ _flags = space.int_w(space.listview(w_args)[0])
+ flags = rffi.cast(rffi.INT_real,_flags)
+ size = generic_cpy_call(space, func_target, w_self, pybuf, flags)
+ if widen(size) < 0:
+ space.fromcache(State).check_and_raise_exception(always=True)
+ ptr = pybuf.c_buf
+ size = pybuf.c_len
+ ndim = widen(pybuf.c_ndim)
+ shape = [pybuf.c_shape[i] for i in range(ndim)]
+ strides = [pybuf.c_strides[i] for i in range(ndim)]
+ if pybuf.c_format:
+ format = rffi.charp2str(pybuf.c_format)
+ else:
+ format = 'B'
+ return space.newbuffer(CPyBuffer(ptr, size, w_self, format=format,
+ ndim=ndim, shape=shape, strides=strides,
+ itemsize=pybuf.c_itemsize,
+ readonly=widen(pybuf.c_readonly)))
+
def get_richcmp_func(OP_CONST):
def inner(space, w_self, w_args, func):
func_target = rffi.cast(richcmpfunc, func)
@@ -486,7 +535,6 @@
def slot_tp_getattro(space, w_self, w_name):
return space.call_function(getattr_fn, w_self, w_name)
api_func = slot_tp_getattro.api_func
-
elif name == 'tp_call':
call_fn = w_type.getdictvalue(space, '__call__')
if call_fn is None:
@@ -542,6 +590,21 @@
w_stararg=w_args, w_starstararg=w_kwds)
return space.call_args(space.get(new_fn, w_self), args)
api_func = slot_tp_new.api_func
+ elif name == 'tp_as_buffer.c_bf_getbuffer':
+ buff_fn = w_type.getdictvalue(space, '__buffer__')
+ if buff_fn is None:
+ return
+ @cpython_api([PyObject, Py_bufferP, rffi.INT_real],
+ rffi.INT_real, header=None, error=-1)
+ @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name))
+ def buff_w(space, w_self, pybuf, flags):
+ # XXX this is wrong, needs a test
+ raise oefmt(space.w_NotImplemented,
+ "calling bf_getbuffer on a builtin type not supported yet")
+ #args = Arguments(space, [w_self],
+ # w_stararg=w_args, w_starstararg=w_kwds)
+ #return space.call_args(space.get(buff_fn, w_self), args)
+ api_func = buff_w.api_func
else:
# missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce
# tp_as_sequence.c_sq_contains, tp_as_sequence.c_sq_length
@@ -850,11 +913,19 @@
slotdefs = eval(slotdefs_str)
# PyPy addition
slotdefs += (
- TPSLOT("__buffer__", "tp_as_buffer.c_bf_getreadbuffer", None, "wrap_getreadbuffer", ""),
+ # XXX that might not be what we want!
+ TPSLOT("__buffer__", "tp_as_buffer.c_bf_getbuffer", None, "wrap_getbuffer", ""),
)
+if not PY3:
+ slotdefs += (
+ TPSLOT("__buffer__", "tp_as_buffer.c_bf_getreadbuffer", None, "wrap_getreadbuffer", ""),
+ )
+
+
# partial sort to solve some slot conflicts:
# Number slots before Mapping slots before Sequence slots.
+# also prefer the new buffer interface
# These are the only conflicts between __name__ methods
def slotdef_sort_key(slotdef):
if slotdef.slot_name.startswith('tp_as_number'):
@@ -863,6 +934,10 @@
return 2
if slotdef.slot_name.startswith('tp_as_sequence'):
return 3
+ if slotdef.slot_name == 'tp_as_buffer.c_bf_getbuffer':
+ return 100
+ if slotdef.slot_name == 'tp_as_buffer.c_bf_getreadbuffer':
+ return 101
return 0
slotdefs = sorted(slotdefs, key=slotdef_sort_key)
diff --git a/pypy/module/cpyext/test/buffer_test.c b/pypy/module/cpyext/test/buffer_test.c
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/test/buffer_test.c
@@ -0,0 +1,243 @@
+#include <Python.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * Adapted from https://jakevdp.github.io/blog/2014/05/05/introduction-to-the-python-buffer-protocol,
+ * which is copyright Jake Vanderplas and released under the BSD license
+ */
+
+/* Structure defines a 1-dimensional strided array */
+typedef struct{
+ int* arr;
+ long length;
+} MyArray;
+
+/* initialize the array with integers 0...length */
+void initialize_MyArray(MyArray* a, long length){
+ int i;
+ a->length = length;
+ a->arr = (int*)malloc(length * sizeof(int));
+ for(i=0; i<length; i++){
+ a->arr[i] = i;
+ }
+}
+
+/* free the memory when finished */
+void deallocate_MyArray(MyArray* a){
+ free(a->arr);
+ a->arr = NULL;
+}
+
+/* tools to print the array */
+char* stringify(MyArray* a, int nmax){
+ char* output = (char*) malloc(nmax * 20);
+ int k, pos = sprintf(&output[0], "[");
+
+ for (k=0; k < a->length && k < nmax; k++){
+ pos += sprintf(&output[pos], " %d", a->arr[k]);
+ }
+ if(a->length > nmax)
+ pos += sprintf(&output[pos], "...");
+ sprintf(&output[pos], " ]");
+ return output;
+}
+
+void print_MyArray(MyArray* a, int nmax){
+ char* s = stringify(a, nmax);
+ printf("%s", s);
+ free(s);
+}
+
+/* This is where we define the PyMyArray object structure */
+typedef struct {
+ PyObject_HEAD
+ /* Type-specific fields go below. */
+ MyArray arr;
+} PyMyArray;
+
+
+/* This is the __init__ function, implemented in C */
+static int
+PyMyArray_init(PyMyArray *self, PyObject *args, PyObject *kwds)
+{
+ // init may have already been called
+ if (self->arr.arr != NULL) {
+ deallocate_MyArray(&self->arr);
+ }
+
+ int length = 0;
+ static char *kwlist[] = {"length", NULL};
+ if (! PyArg_ParseTupleAndKeywords(args, kwds, "|i", kwlist, &length))
+ return -1;
+
+ if (length < 0)
+ length = 0;
+
+ initialize_MyArray(&self->arr, length);
+
+ return 0;
+}
+
+
+/* this function is called when the object is deallocated */
+static void
+PyMyArray_dealloc(PyMyArray* self)
+{
+ deallocate_MyArray(&self->arr);
+ Py_TYPE(self)->tp_free((PyObject*)self);
+}
+
+
+/* This function returns the string representation of our object */
+static PyObject *
+PyMyArray_str(PyMyArray * self)
+{
+ char* s = stringify(&self->arr, 10);
+ PyObject* ret = PyUnicode_FromString(s);
+ free(s);
+ return ret;
+}
+
+/* Here is the buffer interface function */
+static int
+PyMyArray_getbuffer(PyObject *obj, Py_buffer *view, int flags)
+{
+ if (view == NULL) {
+ PyErr_SetString(PyExc_ValueError, "NULL view in getbuffer");
+ return -1;
+ }
+ if (flags == 0) {
+ PyErr_SetString(PyExc_ValueError, "flags == 0 in getbuffer");
+ return -1;
+ }
+
+ PyMyArray* self = (PyMyArray*)obj;
+ view->obj = (PyObject*)self;
+ view->buf = (void*)self->arr.arr;
+ view->len = self->arr.length * sizeof(int);
+ view->readonly = 0;
+ view->itemsize = sizeof(int);
+ view->format = "i"; // integer
+ view->ndim = 1;
+ view->shape = &self->arr.length; // length-1 sequence of dimensions
+ view->strides = &view->itemsize; // for the simple case we can do this
+ view->suboffsets = NULL;
+ view->internal = NULL;
+
+ Py_INCREF(self); // need to increase the reference count
+ return 0;
+}
+
+static PyBufferProcs PyMyArray_as_buffer = {
+#if PY_MAJOR_VERSION < 3
+ (readbufferproc)0,
+ (writebufferproc)0,
+ (segcountproc)0,
+ (charbufferproc)0,
+#endif
+ (getbufferproc)PyMyArray_getbuffer,
+ (releasebufferproc)0, // we do not require any special release function
+};
+
+
+/* Here is the type structure: we put the above functions in the appropriate place
+ in order to actually define the Python object type */
+static PyTypeObject PyMyArrayType = {
+ PyVarObject_HEAD_INIT(NULL, 0)
+ "pymyarray.PyMyArray", /* tp_name */
+ sizeof(PyMyArray), /* tp_basicsize */
+ 0, /* tp_itemsize */
+ (destructor)PyMyArray_dealloc,/* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_reserved */
+ (reprfunc)PyMyArray_str, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ 0, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ (reprfunc)PyMyArray_str, /* tp_str */
+ 0, /* tp_getattro */
+ 0, /* tp_setattro */
+ &PyMyArray_as_buffer, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_NEWBUFFER, /* tp_flags */
+ "PyMyArray object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ 0, /* tp_methods */
+ 0, /* tp_members */
+ 0, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ (initproc)PyMyArray_init, /* tp_init */
+};
+
+static PyMethodDef buffer_functions[] = {
+ {NULL, NULL} /* Sentinel */
+};
+
+#if PY_MAJOR_VERSION >= 3
+static struct PyModuleDef moduledef = {
+ PyModuleDef_HEAD_INIT,
+ "buffer_test",
+ "Module Doc",
+ -1,
+ buffer_functions;
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+#define INITERROR return NULL
+
+/* Initialize this module. */
+#ifdef __GNUC__
+extern __attribute__((visibility("default")))
+#else
+extern __declspec(dllexport)
+#endif
+
+PyMODINIT_FUNC
+PyInit_buffer_test(void)
+
+#else
+
+#define INITERROR return
+
+/* Initialize this module. */
+#ifdef __GNUC__
+extern __attribute__((visibility("default")))
+#else
+extern __declspec(dllexport)
+#endif
+
+PyMODINIT_FUNC
+initbuffer_test(void)
+#endif
+{
+#if PY_MAJOR_VERSION >= 3
+ PyObject *m= PyModule_Create(&moduledef);
+#else
+ PyObject *m= Py_InitModule("buffer_test", buffer_functions);
+#endif
+ if (m == NULL)
+ INITERROR;
+ PyMyArrayType.tp_new = PyType_GenericNew;
+ if (PyType_Ready(&PyMyArrayType) < 0)
+ INITERROR;
+ Py_INCREF(&PyMyArrayType);
+ PyModule_AddObject(m, "PyMyArray", (PyObject *)&PyMyArrayType);
+#if PY_MAJOR_VERSION >=3
+ return m;
+#endif
+}
diff --git a/pypy/module/cpyext/test/test_memoryobject.py b/pypy/module/cpyext/test/test_memoryobject.py
--- a/pypy/module/cpyext/test/test_memoryobject.py
+++ b/pypy/module/cpyext/test/test_memoryobject.py
@@ -1,5 +1,7 @@
import pytest
from pypy.module.cpyext.test.test_api import BaseApiTest
+from pypy.module.cpyext.test.test_cpyext import AppTestCpythonExtensionBase
+
class TestMemoryViewObject(BaseApiTest):
def test_fromobject(self, space, api):
@@ -8,10 +10,23 @@
py.test.skip("unsupported before Python 2.7")
w_hello = space.newbytes("hello")
+ assert api.PyObject_CheckBuffer(w_hello)
w_view = api.PyMemoryView_FromObject(w_hello)
+ w_char = space.call_method(w_view, '__getitem__', space.wrap(0))
+ assert space.eq_w(w_char, space.wrap('h'))
w_bytes = space.call_method(w_view, "tobytes")
assert space.unwrap(w_bytes) == "hello"
- @pytest.mark.skipif(True, reason='write a test for this')
- def test_get_base_and_get_buffer(self, space, api):
- assert False # XXX test PyMemoryView_GET_BASE, PyMemoryView_GET_BUFFER
+
+class AppTestBufferProtocol(AppTestCpythonExtensionBase):
+ def test_buffer_protocol(self):
+ import struct
+ module = self.import_module(name='buffer_test')
+ arr = module.PyMyArray(10)
+ y = memoryview(arr)
+ assert y.format == 'i'
+ assert y.shape == (10,)
+ s = y[3]
+ assert len(s) == struct.calcsize('i')
+ assert s == struct.pack('i', 3)
+
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -17,7 +17,8 @@
generic_cpy_call, Py_TPFLAGS_READY, Py_TPFLAGS_READYING,
Py_TPFLAGS_HEAPTYPE, METH_VARARGS, METH_KEYWORDS, CANNOT_FAIL,
Py_TPFLAGS_HAVE_GETCHARBUFFER, build_type_checkers, StaticObjectBuilder,
- PyObjectFields, Py_TPFLAGS_BASETYPE, PyTypeObject, PyTypeObjectPtr)
+ PyObjectFields, Py_TPFLAGS_BASETYPE, PyTypeObject, PyTypeObjectPtr,
+ Py_TPFLAGS_HAVE_NEWBUFFER)
from pypy.module.cpyext.methodobject import (W_PyCClassMethodObject,
W_PyCWrapperObject, PyCFunction_NewEx, PyCFunction_typedef, PyMethodDef,
W_PyCMethodObject, W_PyCFunctionObject)
@@ -608,6 +609,7 @@
bf_getwritebuffer.api_func.get_wrapper(space))
pto.c_tp_as_buffer = c_buf
pto.c_tp_flags |= Py_TPFLAGS_HAVE_GETCHARBUFFER
+ pto.c_tp_flags |= Py_TPFLAGS_HAVE_NEWBUFFER
@cpython_api([PyObject], lltype.Void, header=None)
def type_dealloc(space, obj):
@@ -774,6 +776,8 @@
pto.c_tp_setattro = base.c_tp_setattro
if not pto.c_tp_getattro:
pto.c_tp_getattro = base.c_tp_getattro
+ if not pto.c_tp_as_buffer:
+ pto.c_tp_as_buffer = base.c_tp_as_buffer
finally:
Py_DecRef(space, base_pyo)
diff --git a/pypy/module/cpyext/typeobjectdefs.py b/pypy/module/cpyext/typeobjectdefs.py
--- a/pypy/module/cpyext/typeobjectdefs.py
+++ b/pypy/module/cpyext/typeobjectdefs.py
@@ -5,6 +5,7 @@
Py_TPFLAGS_READYING, Py_TPFLAGS_READY, Py_TPFLAGS_HEAPTYPE)
from pypy.module.cpyext.pyobject import PyObject, make_ref, from_ref
from pypy.module.cpyext.modsupport import PyMethodDef
+from pypy.module.cpyext.api import Py_bufferP
P, FT, PyO = Ptr, FuncType, PyObject
@@ -58,8 +59,7 @@
writebufferproc = P(FT([PyO, Py_ssize_t, rffi.VOIDPP], Py_ssize_t))
segcountproc = P(FT([PyO, Py_ssize_tP], Py_ssize_t))
charbufferproc = P(FT([PyO, Py_ssize_t, rffi.CCHARPP], Py_ssize_t))
-## We don't support new buffer interface for now
-getbufferproc = rffi.VOIDP
+getbufferproc = P(FT([PyO, Py_bufferP, rffi.INT_real], rffi.INT_real))
releasebufferproc = rffi.VOIDP
diff --git a/pypy/module/micronumpy/compile.py b/pypy/module/micronumpy/compile.py
--- a/pypy/module/micronumpy/compile.py
+++ b/pypy/module/micronumpy/compile.py
@@ -19,7 +19,6 @@
UserDelAction)
from pypy.interpreter.pyframe import PyFrame
-
class BogusBytecode(Exception):
pass
@@ -383,6 +382,9 @@
# XXX even the hacks have hacks
if s == 'size': # used in _array() but never called by tests
return IntObject(0)
+ if s == '__buffer__':
+ # descr___buffer__ does not exist on W_Root
+ return self.w_None
return getattr(w_obj, 'descr_' + s)(self, *args)
@specialize.arg(1)
diff --git a/pypy/module/micronumpy/concrete.py b/pypy/module/micronumpy/concrete.py
--- a/pypy/module/micronumpy/concrete.py
+++ b/pypy/module/micronumpy/concrete.py
@@ -704,3 +704,20 @@
def get_raw_address(self):
from rpython.rtyper.lltypesystem import rffi
return rffi.ptradd(self.impl.storage, self.impl.start)
+
+ def getformat(self):
+ return self.impl.dtype.char
+
+ def getitemsize(self):
+ return self.impl.dtype.elsize
+
+ def getndim(self):
+ return len(self.impl.shape)
+
+ def getshape(self):
+ return self.impl.shape
+
+ def getstrides(self):
+ return self.impl.strides
+
+
diff --git a/pypy/module/micronumpy/ctors.py b/pypy/module/micronumpy/ctors.py
--- a/pypy/module/micronumpy/ctors.py
+++ b/pypy/module/micronumpy/ctors.py
@@ -468,7 +468,8 @@
except OperationError as e:
if not e.match(space, space.w_TypeError):
raise
- w_buffer = space.getattr(w_buffer, space.wrap('__buffer__'))
+ w_buffer = space.call_method(w_buffer, '__buffer__',
+ space.newint(space.BUF_FULL_RO))
buf = _getbuffer(space, w_buffer)
ts = buf.getlength()
diff --git a/pypy/module/micronumpy/test/test_ndarray.py b/pypy/module/micronumpy/test/test_ndarray.py
--- a/pypy/module/micronumpy/test/test_ndarray.py
+++ b/pypy/module/micronumpy/test/test_ndarray.py
@@ -3626,13 +3626,35 @@
assert str(exc.value) == "assignment destination is read-only"
class A(object):
- __buffer__ = 'abc'
+ def __buffer__(self, flags):
+ return 'abc'
data = A()
a = np.frombuffer(data, 'c')
#assert a.base is data.__buffer__
assert a.tostring() == 'abc'
+ def test_memoryview(self):
+ import numpy as np
+ import sys
+ if sys.version_info[:2] > (3, 2):
+ # In Python 3.3 the representation of empty shape, strides and sub-offsets
+ # is an empty tuple instead of None.
+ # http://docs.python.org/dev/whatsnew/3.3.html#api-changes
+ EMPTY = ()
+ else:
+ EMPTY = None
+ x = np.array([1, 2, 3, 4, 5], dtype='i')
+ y = memoryview('abc')
+ assert y.format == 'B'
+ y = memoryview(x)
+ assert y.format == 'i'
+ assert y.shape == (5,)
+ assert y.ndim == 1
+ assert y.strides == (4,)
+ assert y.suboffsets == EMPTY
+ assert y.itemsize == 4
+
def test_fromstring(self):
import sys
from numpy import fromstring, dtype
diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py
--- a/pypy/objspace/std/bytesobject.py
+++ b/pypy/objspace/std/bytesobject.py
@@ -434,7 +434,6 @@
of the specified width. The string S is never truncated.
"""
-
class W_BytesObject(W_AbstractBytesObject):
import_from_mixin(StringMethods)
_immutable_fields_ = ['_value']
@@ -464,6 +463,11 @@
raise oefmt(space.w_TypeError,
"Cannot use string as modifiable buffer")
+ def descr_getbuffer(self, space, w_flags):
+ #from pypy.objspace.std.bufferobject import W_Buffer
+ #return W_Buffer(StringBuffer(self._value))
+ return self
+
charbuf_w = str_w
def listview_bytes(self):
@@ -925,6 +929,7 @@
translate = interpindirect2app(W_AbstractBytesObject.descr_translate),
upper = interpindirect2app(W_AbstractBytesObject.descr_upper),
zfill = interpindirect2app(W_AbstractBytesObject.descr_zfill),
+ __buffer__ = interp2app(W_BytesObject.descr_getbuffer),
format = interpindirect2app(W_BytesObject.descr_format),
__format__ = interpindirect2app(W_BytesObject.descr__format__),
diff --git a/pypy/objspace/std/memoryobject.py b/pypy/objspace/std/memoryobject.py
--- a/pypy/objspace/std/memoryobject.py
+++ b/pypy/objspace/std/memoryobject.py
@@ -73,6 +73,15 @@
def descr_getitem(self, space, w_index):
start, stop, step, size = space.decode_index4(w_index, self.getlength())
+ itemsize = self.buf.getitemsize()
+ if itemsize > 1:
+ start *= itemsize
+ size *= itemsize
+ stop = start + size
+ if step == 0:
+ step = 1
+ if stop > self.getlength():
+ raise oefmt(space.w_IndexError, 'index out of range')
if step not in (0, 1):
raise oefmt(space.w_NotImplementedError, "")
if step == 0: # index only
@@ -85,6 +94,15 @@
if self.buf.readonly:
raise oefmt(space.w_TypeError, "cannot modify read-only memory")
start, stop, step, size = space.decode_index4(w_index, self.getlength())
+ itemsize = self.buf.getitemsize()
+ if itemsize > 1:
+ start *= itemsize
+ size *= itemsize
+ stop = start + size
+ if step == 0:
+ step = 1
+ if stop > self.getlength():
+ raise oefmt(space.w_IndexError, 'index out of range')
if step not in (0, 1):
raise oefmt(space.w_NotImplementedError, "")
value = space.buffer_w(w_obj, space.BUF_CONTIG_RO)
@@ -100,22 +118,22 @@
return space.wrap(self.buf.getlength())
def w_get_format(self, space):
- return space.wrap("B")
+ return space.wrap(self.buf.getformat())
def w_get_itemsize(self, space):
- return space.wrap(1)
+ return space.wrap(self.buf.getitemsize())
def w_get_ndim(self, space):
- return space.wrap(1)
+ return space.wrap(self.buf.getndim())
def w_is_readonly(self, space):
- return space.wrap(self.buf.readonly)
+ return space.newbool(bool(self.buf.readonly))
def w_get_shape(self, space):
- return space.newtuple([space.wrap(self.getlength())])
+ return space.newtuple([space.wrap(x) for x in self.buf.getshape()])
def w_get_strides(self, space):
- return space.newtuple([space.wrap(1)])
+ return space.newtuple([space.wrap(x) for x in self.buf.getstrides()])
def w_get_suboffsets(self, space):
# I've never seen anyone filling this field
diff --git a/pypy/objspace/std/test/test_bufferobject.py b/pypy/objspace/std/test/test_bufferobject.py
--- a/pypy/objspace/std/test/test_bufferobject.py
+++ b/pypy/objspace/std/test/test_bufferobject.py
@@ -4,7 +4,7 @@
def test_init(self):
import sys
class A(object):
- def __buffer__(self):
+ def __buffer__(self, flags):
return buffer('123')
if '__pypy__' not in sys.builtin_module_names:
raises(TypeError, buffer, A())
diff --git a/pypy/tool/pytest/objspace.py b/pypy/tool/pytest/objspace.py
--- a/pypy/tool/pytest/objspace.py
+++ b/pypy/tool/pytest/objspace.py
@@ -107,6 +107,9 @@
def newlist(self, iterable):
return list(iterable)
+ def newbytes(self, obj):
+ return bytes(obj)
+
def call_function(self, func, *args, **kwds):
return func(*args, **kwds)
diff --git a/rpython/rlib/buffer.py b/rpython/rlib/buffer.py
--- a/rpython/rlib/buffer.py
+++ b/rpython/rlib/buffer.py
@@ -59,6 +59,20 @@
def get_raw_address(self):
raise ValueError("no raw buffer")
+ def getformat(self):
+ return 'B'
+
+ def getitemsize(self):
+ return 1
+
+ def getndim(self):
+ return 1
+
+ def getshape(self):
+ return [self.getlength()]
+
+ def getstrides(self):
+ return [1]
class StringBuffer(Buffer):
__slots__ = ['value']
More information about the pypy-commit
mailing list