Author: Ronan Lamy <ronan.l...@gmail.com> Branch: py3.5 Changeset: r90515:531a0caf2bb4 Date: 2017-03-03 18:53 +0100 http://bitbucket.org/pypy/pypy/changeset/531a0caf2bb4/
Log: hg merge default diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst --- a/pypy/doc/whatsnew-head.rst +++ b/pypy/doc/whatsnew-head.rst @@ -176,3 +176,9 @@ .. branch: py3.5-text-utf8 space.text_w now encodes to utf-8 not preserving surrogates. + +.. branch: fix-cpyext-releasebuffer + +Improve handling of the Py3-style buffer slots in cpyext: fix memoryviews +keeping objects alive forever (missing decref), and make sure that +bf_releasebuffer is called when it should, e.g. from PyBuffer_Release. diff --git a/pypy/module/_io/test/test_fileio.py b/pypy/module/_io/test/test_fileio.py --- a/pypy/module/_io/test/test_fileio.py +++ b/pypy/module/_io/test/test_fileio.py @@ -6,12 +6,12 @@ class AppTestFileIO: spaceconfig = dict(usemodules=['_io'] + (['fcntl'] if os.name != 'nt' else [])) - def setup_class(cls): + def setup_method(self, meth): tmpfile = udir.join('tmpfile') tmpfile.write("a\nb\nc", mode='wb') - cls.w_tmpfile = cls.space.wrap(str(tmpfile)) - cls.w_tmpdir = cls.space.wrap(str(udir)) - cls.w_posix = cls.space.appexec([], """(): + self.w_tmpfile = self.space.wrap(str(tmpfile)) + self.w_tmpdir = self.space.wrap(str(udir)) + self.w_posix = self.space.appexec([], """(): import %s as m; return m""" % os.name) @@ -154,7 +154,7 @@ f.seek(5) f.write(b'\x00' * 5) f.seek(0) - assert f.readinto(a) == 10 + assert f.readinto(a) == 5 f.seek(0) m = memoryview(bytearray(b"helloworld")) assert f.readinto(m) == 10 @@ -170,7 +170,7 @@ assert " read-write b" in msg and msg.endswith(", not memoryview") # f.close() - assert a == b'a\nb\nc\0\0\0\0\0' + assert a == b'a\nb\ncxxxxx' # a = bytearray(b'x' * 10) f = _io.FileIO(self.tmpfile, 'r+') 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 @@ -579,7 +579,7 @@ 'PyObject_CallMethod', 'PyObject_CallFunctionObjArgs', 'PyObject_CallMethodObjArgs', '_PyObject_CallFunction_SizeT', '_PyObject_CallMethod_SizeT', - 'PyBuffer_Release', + 'PyObject_GetBuffer', 'PyBuffer_Release', 'PyCObject_FromVoidPtr', 'PyCObject_FromVoidPtrAndDesc', 'PyCObject_AsVoidPtr', 'PyCObject_GetDesc', 'PyCObject_Import', 'PyCObject_SetVoidPtr', diff --git a/pypy/module/cpyext/include/abstract.h b/pypy/module/cpyext/include/abstract.h --- a/pypy/module/cpyext/include/abstract.h +++ b/pypy/module/cpyext/include/abstract.h @@ -1,5 +1,8 @@ #ifndef Py_ABSTRACTOBJECT_H #define Py_ABSTRACTOBJECT_H +#ifdef __cplusplus +extern "C" { +#endif /* new buffer API */ @@ -10,9 +13,22 @@ /* Return 1 if the getbuffer function is available, otherwise return 0 */ + PyAPI_FUNC(int) PyObject_GetBuffer(PyObject *obj, Py_buffer *view, + int flags); + + /* This is a C-API version of the getbuffer function call. It checks + to make sure object has the required function pointer and issues the + call. Returns -1 and raises an error on failure and returns 0 on + success + */ + PyAPI_FUNC(void) PyBuffer_Release(Py_buffer *view); /* Releases a Py_buffer obtained from getbuffer ParseTuple's s*. */ + +#ifdef __cplusplus +} +#endif #endif /* Py_ABSTRACTOBJECT_H */ 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 @@ -1,9 +1,11 @@ +from rpython.rlib.objectmodel import keepalive_until_here +from pypy.interpreter.error import oefmt from pypy.module.cpyext.api import ( cpython_api, Py_buffer, CANNOT_FAIL, Py_MAX_FMT, Py_MAX_NDIMS, build_type_checkers, Py_ssize_tP, PyObjectFields, cpython_struct, - bootstrap_function, Py_bufferP, generic_cpy_call, slot_function) + bootstrap_function, Py_bufferP, slot_function, generic_cpy_call) from pypy.module.cpyext.pyobject import ( - PyObject, make_ref, as_pyobj, incref, decref, from_ref, make_typedescr, + PyObject, make_ref, as_pyobj, decref, from_ref, make_typedescr, get_typedescr, track_reference) from rpython.rtyper.lltypesystem import lltype, rffi from rpython.rlib.rarithmetic import widen @@ -94,30 +96,6 @@ mem_obj.c_view.c_obj = rffi.cast(PyObject, 0) _dealloc(space, py_obj) -@cpython_api([PyObject, Py_bufferP, rffi.INT_real], - rffi.INT_real, error=-1) -def PyObject_GetBuffer(space, exporter, view, flags): - """Send a request to exporter to fill in view as specified by flags. If the - exporter cannot provide a buffer of the exact type, it MUST raise - PyExc_BufferError, set view->obj to NULL and return -1. - - On success, fill in view, set view->obj to a new reference to exporter and - return 0. In the case of chained buffer providers that redirect requests - to a single object, view->obj MAY refer to this object instead of exporter. - - Successful calls to PyObject_GetBuffer() must be paired with calls to - PyBuffer_Release(), similar to malloc() and free(). Thus, after the - consumer is done with the buffer, PyBuffer_Release() must be called exactly - once. - """ - # XXX compare this implementation with PyPy2's XXX - pb = exporter.c_ob_type.c_tp_as_buffer - if not pb or not pb.c_bf_getbuffer: - w_exporter = from_ref(space, exporter) - raise oefmt(space.w_TypeError, - "a bytes-like object is required, not '%T'", w_exporter) - return generic_cpy_call(space, pb.c_bf_getbuffer, exporter, view, flags) - def fill_Py_buffer(space, buf, view): # c_buf, c_obj have been filled in ndim = buf.getndim() 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 @@ -7,14 +7,14 @@ from rpython.rlib import rgc # Force registration of gc.collect from pypy.module.cpyext.api import ( slot_function, generic_cpy_call, PyObject, Py_ssize_t, - pypy_decl, Py_buffer, Py_bufferP, PyTypeObjectPtr) + pypy_decl, Py_buffer, Py_bufferP, PyTypeObjectPtr, cts) from pypy.module.cpyext.typeobjectdefs import ( unaryfunc, ternaryfunc, binaryfunc, getattrfunc, getattrofunc, setattrofunc, lenfunc, ssizeargfunc, inquiry, ssizessizeargfunc, ssizeobjargproc, iternextfunc, initproc, richcmpfunc, cmpfunc, hashfunc, descrgetfunc, descrsetfunc, objobjproc, objobjargproc, getbufferproc, releasebufferproc, ssizessizeobjargproc) -from pypy.module.cpyext.pyobject import make_ref, decref +from pypy.module.cpyext.pyobject import make_ref, decref, as_pyobj from pypy.module.cpyext.pyerrors import PyErr_Occurred from pypy.module.cpyext.memoryobject import fill_Py_buffer from pypy.module.cpyext.state import State @@ -318,13 +318,14 @@ _immutable_ = True def __init__(self, space, ptr, size, w_obj, format='B', shape=None, - strides=None, ndim=1, itemsize=1, readonly=True, - releasebufferproc=rffi.cast(rffi.VOIDP, 0)): + strides=None, ndim=1, itemsize=1, readonly=True, + needs_decref=False, + releasebufferproc=rffi.cast(rffi.VOIDP, 0)): self.space = space self.ptr = ptr self.size = size self.w_obj = w_obj # kept alive - self.pyobj = make_ref(space, w_obj) + self.pyobj = as_pyobj(space, w_obj) self.format = format self.ndim = ndim self.itemsize = itemsize @@ -348,30 +349,33 @@ # XXX: missing init_strides_from_shape self.strides = strides self.readonly = readonly + self.needs_decref = needs_decref self.releasebufferproc = releasebufferproc def releasebuffer(self): if self.pyobj: - decref(self.space, self.pyobj) + if self.needs_decref: + if self.releasebufferproc: + func_target = rffi.cast(releasebufferproc, self.releasebufferproc) + with lltype.scoped_alloc(Py_buffer) as pybuf: + pybuf.c_buf = self.ptr + pybuf.c_len = self.size + pybuf.c_ndim = cts.cast('int', self.ndim) + pybuf.c_shape = cts.cast('Py_ssize_t*', pybuf.c__shape) + pybuf.c_strides = cts.cast('Py_ssize_t*', pybuf.c__strides) + for i in range(self.ndim): + pybuf.c_shape[i] = self.shape[i] + pybuf.c_strides[i] = self.strides[i] + if self.format: + pybuf.c_format = rffi.str2charp(self.format) + else: + pybuf.c_format = rffi.str2charp("B") + generic_cpy_call(self.space, func_target, self.pyobj, pybuf) + decref(self.space, self.pyobj) self.pyobj = lltype.nullptr(PyObject.TO) else: #do not call twice return - if self.releasebufferproc: - func_target = rffi.cast(releasebufferproc, self.releasebufferproc) - with lltype.scoped_alloc(Py_buffer) as pybuf: - pybuf.c_buf = self.ptr - pybuf.c_len = self.size - pybuf.c_ndim = rffi.cast(rffi.INT_real, self.ndim) - for i in range(self.ndim): - pybuf.c_shape[i] = self.shape[i] - pybuf.c_strides[i] = self.strides[i] - if self.format: - pybuf.c_format = rffi.str2charp(self.format) - else: - pybuf.c_format = rffi.str2charp("B") - generic_cpy_call(self.space, func_target, self.pyobj, pybuf) - self.releasebufferproc = rffi.cast(rffi.VOIDP, 0) def getlength(self): return self.size @@ -467,22 +471,25 @@ ptr = pybuf.c_buf size = pybuf.c_len ndim = widen(pybuf.c_ndim) + shape = None if pybuf.c_shape: - shape = [pybuf.c_shape[i] for i in range(ndim)] - else: - shape = None + shape = [pybuf.c_shape[i] for i in range(ndim)] + strides = None if pybuf.c_strides: strides = [pybuf.c_strides[i] for i in range(ndim)] - else: - strides = [1] if pybuf.c_format: format = rffi.charp2str(pybuf.c_format) else: format = 'B' + # the CPython docs mandates that you do an incref whenever you call + # bf_getbuffer; so, we pass needs_decref=True to ensure that we don't + # leak we release the buffer: + # https://docs.python.org/3.5/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer buf = CPyBuffer(space, ptr, size, w_self, format=format, ndim=ndim, shape=shape, strides=strides, itemsize=pybuf.c_itemsize, readonly=widen(pybuf.c_readonly), + needs_decref=True, releasebufferproc = rbp) fq.register_finalizer(buf) return space.newbuffer(buf, itemsize=buf.itemsize) @@ -717,33 +724,12 @@ slot_func = slot_tp_new elif name == 'tp_as_buffer.c_bf_getbuffer': buff_fn = w_type.getdictvalue(space, '__buffer__') - if buff_fn is None: + if buff_fn is not None: + buff_w = slot_from___buffer__(space, typedef, buff_fn) + elif typedef.buffer: + buff_w = slot_from_buffer_w(space, typedef, buff_fn) + else: return - @slot_function([PyObject, Py_bufferP, rffi.INT_real], - rffi.INT_real, error=-1) - @func_renamer("cpyext_%s_%s" % (name.replace('.', '_'), typedef.name)) - def buff_w(space, w_self, view, flags): - args = Arguments(space, [space.newint(flags)]) - w_obj = space.call_args(space.get(buff_fn, w_self), args) - if view: - #like PyObject_GetBuffer - flags = widen(flags) - buf = space.buffer_w(w_obj, flags) - try: - view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address()) - view.c_obj = make_ref(space, w_obj) - except ValueError: - s = buf.as_str() - w_s = space.newbytes(s) - view.c_obj = make_ref(space, w_s) - view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp( - s, track_allocation=False)) - rffi.setintfield(view, 'c_readonly', 1) - ret = fill_Py_buffer(space, buf, view) - return ret - return 0 - # XXX remove this when it no longer crashes a translated PyPy - return slot_func = buff_w else: # missing: tp_as_number.nb_nonzero, tp_as_number.nb_coerce @@ -753,6 +739,60 @@ return slot_func + +def slot_from___buffer__(space, typedef, buff_fn): + name = 'bf_getbuffer' + @slot_function([PyObject, Py_bufferP, rffi.INT_real], + rffi.INT_real, error=-1) + @func_renamer("cpyext_%s_%s" % (name, typedef.name)) + def buff_w(space, w_self, view, flags): + args = Arguments(space, [space.newint(flags)]) + w_obj = space.call_args(space.get(buff_fn, w_self), args) + if view: + #like PyObject_GetBuffer + flags = widen(flags) + buf = space.buffer_w(w_obj, flags) + try: + view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address()) + view.c_obj = make_ref(space, w_obj) + except ValueError: + s = buf.as_str() + w_s = space.newbytes(s) + view.c_obj = make_ref(space, w_s) + view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp( + s, track_allocation=False)) + rffi.setintfield(view, 'c_readonly', 1) + ret = fill_Py_buffer(space, buf, view) + return ret + return 0 + return buff_w + +def slot_from_buffer_w(space, typedef, buff_fn): + name = 'bf_getbuffer' + @slot_function([PyObject, Py_bufferP, rffi.INT_real], + rffi.INT_real, error=-1) + @func_renamer("cpyext_%s_%s" % (name, typedef.name)) + def buff_w(space, w_self, view, flags): + w_obj = w_self + if view: + #like PyObject_GetBuffer + flags = widen(flags) + buf = space.buffer_w(w_obj, flags) + try: + view.c_buf = rffi.cast(rffi.VOIDP, buf.get_raw_address()) + view.c_obj = make_ref(space, w_obj) + except ValueError: + s = buf.as_str() + w_s = space.newbytes(s) + view.c_obj = make_ref(space, w_s) + view.c_buf = rffi.cast(rffi.VOIDP, rffi.str2charp( + s, track_allocation=False)) + rffi.setintfield(view, 'c_readonly', 1) + ret = fill_Py_buffer(space, buf, view) + return ret + return 0 + return buff_w + PyWrapperFlag_KEYWORDS = 1 class TypeSlot: diff --git a/pypy/module/cpyext/src/abstract.c b/pypy/module/cpyext/src/abstract.c --- a/pypy/module/cpyext/src/abstract.c +++ b/pypy/module/cpyext/src/abstract.c @@ -98,6 +98,18 @@ /* Buffer C-API for Python 3.0 */ +int +PyObject_GetBuffer(PyObject *obj, Py_buffer *view, int flags) +{ + if (!PyObject_CheckBuffer(obj)) { + PyErr_Format(PyExc_TypeError, + "'%100s' does not have the buffer interface", + Py_TYPE(obj)->tp_name); + return -1; + } + return (*(obj->ob_type->tp_as_buffer->bf_getbuffer))(obj, view, flags); +} + void PyBuffer_Release(Py_buffer *view) { diff --git a/pypy/module/cpyext/test/array.c b/pypy/module/cpyext/test/array.c --- a/pypy/module/cpyext/test/array.c +++ b/pypy/module/cpyext/test/array.c @@ -2351,10 +2351,10 @@ } else if(obj1->ob_type == &Arraytype) fprintf(stderr, "\nCannot multiply array of type %c and %s\n", - ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name); + ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name); else if(obj2->ob_type == &Arraytype) fprintf(stderr, "\nCannot multiply array of type %c and %s\n", - ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name); + ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name); Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } @@ -2401,10 +2401,10 @@ } else if(obj1->ob_type == &Arraytype) fprintf(stderr, "\nCannot multiply array of type %c and %s\n", - ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name); + ((arrayobject*)obj1)->ob_descr->typecode, obj2->ob_type->tp_name); else if(obj2->ob_type == &Arraytype) fprintf(stderr, "\nCannot multiply array of type %c and %s\n", - ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name); + ((arrayobject*)obj2)->ob_descr->typecode, obj1->ob_type->tp_name); Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } @@ -2426,7 +2426,7 @@ static int -array_buffer_getbuf(arrayobject *self, Py_buffer *view, int flags) +array_getbuffer(arrayobject *self, Py_buffer *view, int flags) { if (view==NULL) goto finish; @@ -2463,10 +2463,19 @@ return 0; } +static long releasebuffer_cnt = 0; + +static PyObject * +get_releasebuffer_cnt(void) +{ + return PyLong_FromLong(releasebuffer_cnt); +} + static void -array_buffer_relbuf(arrayobject *self, Py_buffer *view) +array_releasebuffer(arrayobject *self, Py_buffer *view) { self->ob_exports--; + releasebuffer_cnt++; } static PySequenceMethods array_as_sequence = { @@ -2483,8 +2492,8 @@ }; static PyBufferProcs array_as_buffer = { - (getbufferproc)array_buffer_getbuf, - (releasebufferproc)array_buffer_relbuf + (getbufferproc)array_getbuffer, + (releasebufferproc)array_releasebuffer }; static PyObject * @@ -2872,6 +2881,16 @@ } static PyObject * +create_and_release_buffer(PyObject *self, PyObject *obj) +{ + Py_buffer view; + int res = PyObject_GetBuffer(obj, &view, 0); + if (res < 0) + return NULL; + PyBuffer_Release(&view); + Py_RETURN_NONE; +} +static PyObject * write_buffer_len(PyObject * self, PyObject * obj) { void* buf; @@ -2890,6 +2909,8 @@ PyDoc_STR("Internal. Used for pickling support.")}, {"switch_multiply", (PyCFunction)switch_multiply, METH_NOARGS, NULL}, {"readbuffer_as_string", (PyCFunction)readbuffer_as_string, METH_VARARGS, NULL}, + {"get_releasebuffer_cnt", (PyCFunction)get_releasebuffer_cnt, METH_NOARGS, NULL}, + {"create_and_release_buffer", (PyCFunction)create_and_release_buffer, METH_O, NULL}, {"write_buffer_len", write_buffer_len, METH_O, NULL}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/pypy/module/cpyext/test/test_arraymodule.py b/pypy/module/cpyext/test/test_arraymodule.py --- a/pypy/module/cpyext/test/test_arraymodule.py +++ b/pypy/module/cpyext/test/test_arraymodule.py @@ -69,6 +69,23 @@ b'\x03\0\0\0' b'\x04\0\0\0') + def test_releasebuffer(self): + module = self.import_module(name='array') + arr = module.array('i', [1,2,3,4]) + assert module.get_releasebuffer_cnt() == 0 + module.create_and_release_buffer(arr) + assert module.get_releasebuffer_cnt() == 1 + + def test_Py_buffer(self): + module = self.import_module(name='array') + arr = module.array('i', [1,2,3,4]) + assert module.get_releasebuffer_cnt() == 0 + m = memoryview(arr) + assert module.get_releasebuffer_cnt() == 0 + del m + self.debug_collect() + assert module.get_releasebuffer_cnt() == 1 + def test_0d_view(self): module = self.import_module(name='array') arr = module.array('B', b'\0\0\0\x01') 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 @@ -10,15 +10,6 @@ only_pypy ="config.option.runappdirect and '__pypy__' not in sys.builtin_module_names" class TestMemoryViewObject(BaseApiTest): - def test_fromobject(self, space, api): - w_hello = space.newbytes("hello") - #assert api.PyObject_CheckBuffer(w_hello) - w_view = from_ref(space, api.PyMemoryView_FromObject(w_hello)) - w_byte = space.call_method(w_view, '__getitem__', space.wrap(0)) - assert space.eq_w(w_byte, space.wrap(ord('h'))) - w_bytes = space.call_method(w_view, "tobytes") - assert space.unwrap(w_bytes) == "hello" - def test_frombuffer(self, space, api): w_buf = space.newbuffer(StringBuffer("hello")) c_memoryview = rffi.cast( @@ -96,6 +87,19 @@ assert result.tobytes() == b'hello, world.' class AppTestBufferProtocol(AppTestCpythonExtensionBase): + def test_fromobject(self): + foo = self.import_extension('foo', [ + ("make_view", "METH_O", + """ + if (!PyObject_CheckBuffer(args)) + return Py_None; + return PyMemoryView_FromObject(args); + """)]) + hello = b'hello' + mview = foo.make_view(hello) + assert mview[0] == hello[0] + assert mview.tobytes() == hello + def test_buffer_protocol_app(self): module = self.import_module(name='buffer_test') arr = module.PyMyArray(10) @@ -183,3 +187,75 @@ " on too long format string" finally: warnings.resetwarnings() + + def test_releasebuffer(self): + if not self.runappdirect: + skip("Fails due to ll2ctypes nonsense") + module = self.import_extension('foo', [ + ("create_test", "METH_NOARGS", + """ + PyObject *obj; + obj = PyObject_New(PyObject, (PyTypeObject*)type); + return obj; + """), + ("get_cnt", "METH_NOARGS", + 'return PyLong_FromLong(cnt);'), + ("get_dealloc_cnt", "METH_NOARGS", + 'return PyLong_FromLong(dealloc_cnt);'), + ], + prologue=""" + static float test_data = 42.f; + static int cnt=0; + static int dealloc_cnt=0; + static PyHeapTypeObject * type=NULL; + + void dealloc(PyObject *self) { + dealloc_cnt++; + } + int getbuffer(PyObject *obj, Py_buffer *view, int flags) { + + cnt ++; + memset(view, 0, sizeof(Py_buffer)); + view->obj = obj; + /* see the CPython docs for why we need this incref: + https://docs.python.org/3.5/c-api/typeobj.html#c.PyBufferProcs.bf_getbuffer */ + Py_INCREF(obj); + view->ndim = 0; + view->buf = (void *) &test_data; + view->itemsize = sizeof(float); + view->len = 1; + view->strides = NULL; + view->shape = NULL; + view->format = "f"; + return 0; + } + + void releasebuffer(PyObject *obj, Py_buffer *view) { + cnt --; + } + """, more_init=""" + type = (PyHeapTypeObject *) PyType_Type.tp_alloc(&PyType_Type, 0); + + type->ht_type.tp_name = "Test"; + type->ht_type.tp_basicsize = sizeof(PyObject); + type->ht_name = PyString_FromString("Test"); + type->ht_type.tp_flags |= Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | + Py_TPFLAGS_HEAPTYPE | Py_TPFLAGS_HAVE_NEWBUFFER; + type->ht_type.tp_flags &= ~Py_TPFLAGS_HAVE_GC; + + type->ht_type.tp_dealloc = dealloc; + type->ht_type.tp_as_buffer = &type->as_buffer; + type->as_buffer.bf_getbuffer = getbuffer; + type->as_buffer.bf_releasebuffer = releasebuffer; + + if (PyType_Ready(&type->ht_type) < 0) INITERROR; + """, ) + import gc + assert module.get_cnt() == 0 + a = memoryview(module.create_test()) + assert module.get_cnt() == 1 + assert module.get_dealloc_cnt() == 0 + del a + self.debug_collect() + assert module.get_cnt() == 0 + assert module.get_dealloc_cnt() == 1 diff --git a/pypy/module/micronumpy/ndarray.py b/pypy/module/micronumpy/ndarray.py --- a/pypy/module/micronumpy/ndarray.py +++ b/pypy/module/micronumpy/ndarray.py @@ -1513,7 +1513,7 @@ return res """, filename=__file__).interphook('ptp') -W_NDimArray.typedef = TypeDef("numpy.ndarray", +W_NDimArray.typedef = TypeDef("numpy.ndarray", None, None, 'read-write', __new__ = interp2app(descr_new_array), __len__ = interp2app(W_NDimArray.descr_len), diff --git a/rpython/rlib/debug.py b/rpython/rlib/debug.py --- a/rpython/rlib/debug.py +++ b/rpython/rlib/debug.py @@ -524,7 +524,8 @@ ll_attach = rffi.llexternal("AttachToVS", [], lltype.Void, compilation_info=make_vs_attach_eci()) def impl_attach_gdb(): - ll_attach() + #ll_attach() + print "AttachToVS is disabled at the moment (compilation failure)" register_external(attach_gdb, [], result=None, export_name="impl_attach_gdb", llimpl=impl_attach_gdb) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit