@@ -466,6 +466,79 @@ class TestPyMemoryView(CPyExtTestCase):
466466
467467
468468class TestObject (unittest .TestCase ):
469+ def test_memoryview_native_exports (self ):
470+ TestType = CPyExtType (
471+ "TestMemoryViewNativeExports" ,
472+ """
473+ static PyObject *acquire(PyObject *self, PyObject *args) {
474+ PyObject *obj;
475+ int flags;
476+ if (!PyArg_ParseTuple(args, "Oi", &obj, &flags)) {
477+ return NULL;
478+ }
479+ TestMemoryViewNativeExportsObject *holder = (TestMemoryViewNativeExportsObject *)self;
480+ if (holder->view.obj != NULL) {
481+ PyErr_SetString(PyExc_RuntimeError, "buffer already acquired");
482+ return NULL;
483+ }
484+ if (PyObject_GetBuffer(obj, &holder->view, flags) < 0) {
485+ return NULL;
486+ }
487+ Py_buffer *v = &holder->view;
488+ return Py_BuildValue("nniiii", v->len, v->itemsize, v->ndim,
489+ v->format != NULL, v->shape != NULL, v->strides != NULL);
490+ }
491+ static PyObject *drop(PyObject *self, PyObject *unused) {
492+ PyBuffer_Release(&((TestMemoryViewNativeExportsObject *)self)->view);
493+ Py_RETURN_NONE;
494+ }
495+ static PyObject *read_buffer(PyObject *self, PyObject *unused) {
496+ Py_buffer *v = &((TestMemoryViewNativeExportsObject *)self)->view;
497+ PyObject *result = PyBytes_FromStringAndSize(NULL, v->len);
498+ if (result != NULL && PyBuffer_ToContiguous(PyBytes_AS_STRING(result), v, v->len, 'C') < 0) {
499+ Py_CLEAR(result);
500+ }
501+ return result;
502+ }
503+ """ ,
504+ cmembers = 'Py_buffer view;' ,
505+ tp_methods = '''
506+ {"acquire", acquire, METH_VARARGS, ""},
507+ {"drop", drop, METH_NOARGS, ""},
508+ {"read", read_buffer, METH_NOARGS, ""}
509+ ''' ,
510+ )
511+ first , second = TestType (), TestType ()
512+ mv = memoryview (bytearray (b'abcdefgh' ))[1 :7 ].cast ('H' , shape = [1 , 3 ])
513+ assert first .acquire (mv , 0x11c ) == (6 , 2 , 2 , 1 , 1 , 1 ) # PyBUF_FULL_RO
514+ assert second .acquire (mv , 0 ) == (6 , 2 , 1 , 0 , 0 , 0 ) # PyBUF_SIMPLE
515+ assert first .read () == b'bcdefg'
516+ with self .assertRaises (BufferError ):
517+ mv .release ()
518+ first .drop ()
519+ with self .assertRaises (BufferError ):
520+ mv .release ()
521+ second .drop ()
522+ mv .release ()
523+ with self .assertRaises (ValueError ):
524+ first .acquire (mv , 0x11c )
525+
526+ for mv , flags in [(memoryview (b'abc' ), 1 ), # PyBUF_WRITABLE
527+ (memoryview (b'abcdef' )[::2 ], 0 ),
528+ (memoryview (b'abcdef' )[::2 ], 0x38 ), # PyBUF_C_CONTIGUOUS
529+ (memoryview (b'abcdef' )[::2 ], 0x58 ), # PyBUF_F_CONTIGUOUS
530+ (memoryview (b'abcdef' )[::2 ], 0x98 ), # PyBUF_ANY_CONTIGUOUS
531+ (memoryview (b'abc' ), 4 )]: # PyBUF_SIMPLE | PyBUF_FORMAT
532+ with self .assertRaises (BufferError ):
533+ first .acquire (mv , flags )
534+ mv .release () # Failed acquisition must not leave an export behind.
535+
536+ mv = memoryview (bytearray (b'abcdef' ))[::2 ]
537+ assert first .acquire (mv , 0x11c ) == (3 , 1 , 1 , 1 , 1 , 1 )
538+ assert first .read () == b'ace'
539+ first .drop ()
540+ mv .release ()
541+
469542 def test_memoryview_fromobject_multidim (self ):
470543 TestType = CPyExtType (
471544 "TestMemoryViewMultidim" ,
0 commit comments