gh-102500: Implement PEP 688 (#102521)

Co-authored-by: Kumar Aditya <59607654+kumaraditya303@users.noreply.github.com>
This commit is contained in:
Jelle Zijlstra 2023-05-04 07:59:46 -07:00 • committed by GitHub
parent b17d32c114
commit 04f6733275
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 640 additions and 15 deletions

View file

@ -62,6 +62,66 @@ exit:
return return_value;
}
PyDoc_STRVAR(memoryview__from_flags__doc__,
"_from_flags($type, /, object, flags)\n"
"--\n"
"\n"
"Create a new memoryview object which references the given object.");
#define MEMORYVIEW__FROM_FLAGS_METHODDEF \
{"_from_flags", _PyCFunction_CAST(memoryview__from_flags), METH_FASTCALL|METH_KEYWORDS|METH_CLASS, memoryview__from_flags__doc__},
static PyObject *
memoryview__from_flags_impl(PyTypeObject *type, PyObject *object, int flags);
static PyObject *
memoryview__from_flags(PyTypeObject *type, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
#define NUM_KEYWORDS 2
static struct {
PyGC_Head _this_is_not_used;
PyObject_VAR_HEAD
PyObject *ob_item[NUM_KEYWORDS];
} _kwtuple = {
.ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
.ob_item = { &_Py_ID(object), &_Py_ID(flags), },
};
#undef NUM_KEYWORDS
#define KWTUPLE (&_kwtuple.ob_base.ob_base)
#else // !Py_BUILD_CORE
# define KWTUPLE NULL
#endif // !Py_BUILD_CORE
static const char * const _keywords[] = {"object", "flags", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "_from_flags",
.kwtuple = KWTUPLE,
};
#undef KWTUPLE
PyObject *argsbuf[2];
PyObject *object;
int flags;
args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, argsbuf);
if (!args) {
goto exit;
}
object = args[0];
flags = _PyLong_AsInt(args[1]);
if (flags == -1 && PyErr_Occurred()) {
goto exit;
}
return_value = memoryview__from_flags_impl(type, object, flags);
exit:
return return_value;
}
PyDoc_STRVAR(memoryview_release__doc__,
"release($self, /)\n"
"--\n"
@ -356,4 +416,4 @@ skip_optional_pos:
exit:
return return_value;
}
/*[clinic end generated code: output=a832f2fc44e4794c input=a9049054013a1b77]*/
/*[clinic end generated code: output=01613814112cedd7 input=a9049054013a1b77]*/

View file

@ -85,7 +85,7 @@ mbuf_alloc(void)
}
static PyObject *
_PyManagedBuffer_FromObject(PyObject *base)
_PyManagedBuffer_FromObject(PyObject *base, int flags)
{
_PyManagedBufferObject *mbuf;
@ -93,7 +93,7 @@ _PyManagedBuffer_FromObject(PyObject *base)
if (mbuf == NULL)
return NULL;
if (PyObject_GetBuffer(base, &mbuf->master, PyBUF_FULL_RO) < 0) {
if (PyObject_GetBuffer(base, &mbuf->master, flags) < 0) {
mbuf->master.obj = NULL;
Py_DECREF(mbuf);
return NULL;
@ -777,11 +777,12 @@ PyMemoryView_FromBuffer(const Py_buffer *info)
return mv;
}
/* Create a memoryview from an object that implements the buffer protocol.
/* Create a memoryview from an object that implements the buffer protocol,
using the given flags.
If the object is a memoryview, the new memoryview must be registered
with the same managed buffer. Otherwise, a new managed buffer is created. */
PyObject *
PyMemoryView_FromObject(PyObject *v)
PyMemoryView_FromObjectAndFlags(PyObject *v, int flags)
{
_PyManagedBufferObject *mbuf;
@ -792,7 +793,7 @@ PyMemoryView_FromObject(PyObject *v)
}
else if (PyObject_CheckBuffer(v)) {
PyObject *ret;
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(v);
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(v, flags);
if (mbuf == NULL)
return NULL;
ret = mbuf_add_view(mbuf, NULL);
@ -805,6 +806,14 @@ PyMemoryView_FromObject(PyObject *v)
Py_TYPE(v)->tp_name);
return NULL;
}
/* Create a memoryview from an object that implements the buffer protocol.
If the object is a memoryview, the new memoryview must be registered
with the same managed buffer. Otherwise, a new managed buffer is created. */
PyObject *
PyMemoryView_FromObject(PyObject *v)
{
return PyMemoryView_FromObjectAndFlags(v, PyBUF_FULL_RO);
}
/* Copy the format string from a base object that might vanish. */
static int
@ -851,7 +860,7 @@ memory_from_contiguous_copy(const Py_buffer *src, char order)
if (bytes == NULL)
return NULL;
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(bytes);
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(bytes, PyBUF_FULL_RO);
Py_DECREF(bytes);
if (mbuf == NULL)
return NULL;
@ -968,6 +977,24 @@ memoryview_impl(PyTypeObject *type, PyObject *object)
}
/*[clinic input]
@classmethod
memoryview._from_flags
object: object
flags: int
Create a new memoryview object which references the given object.
[clinic start generated code]*/
static PyObject *
memoryview__from_flags_impl(PyTypeObject *type, PyObject *object, int flags)
/*[clinic end generated code: output=bf71f9906c266ee2 input=f5f82fd0e744356b]*/
{
return PyMemoryView_FromObjectAndFlags(object, flags);
}
/****************************************************************************/
/* Previously in abstract.c */
/****************************************************************************/
@ -3184,6 +3211,7 @@ static PyMethodDef memory_methods[] = {
MEMORYVIEW_TOLIST_METHODDEF
MEMORYVIEW_CAST_METHODDEF
MEMORYVIEW_TOREADONLY_METHODDEF
MEMORYVIEW__FROM_FLAGS_METHODDEF
{"__enter__", memory_enter, METH_NOARGS, NULL},
{"__exit__", memory_exit, METH_VARARGS, NULL},
{NULL, NULL}

View file

@ -14,6 +14,7 @@
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_symtable.h" // PySTEntry_Type
#include "pycore_typeobject.h" // _PyBufferWrapper_Type
#include "pycore_unionobject.h" // _PyUnion_Type
#include "pycore_interpreteridobject.h" // _PyInterpreterID_Type
@ -2084,6 +2085,7 @@ static PyTypeObject* static_types[] = {
&_PyAsyncGenASend_Type,
&_PyAsyncGenAThrow_Type,
&_PyAsyncGenWrappedValue_Type,
&_PyBufferWrapper_Type,
&_PyContextTokenMissing_Type,
&_PyCoroWrapper_Type,
&_Py_GenericAliasIterType,

View file

@ -6,6 +6,7 @@
#include "pycore_symtable.h" // _Py_Mangle()
#include "pycore_dict.h" // _PyDict_KeysSize()
#include "pycore_initconfig.h" // _PyStatus_OK()
#include "pycore_memoryobject.h" // PyMemoryView_FromObjectAndFlags()
#include "pycore_moduleobject.h" // _PyModule_GetDef()
#include "pycore_object.h" // _PyType_HasFeature()
#include "pycore_long.h" // _PyLong_IsNegative()
@ -8059,6 +8060,58 @@ wrap_descr_delete(PyObject *self, PyObject *args, void *wrapped)
Py_RETURN_NONE;
}
static PyObject *
wrap_buffer(PyObject *self, PyObject *args, void *wrapped)
{
PyObject *arg = NULL;
if (!PyArg_UnpackTuple(args, "", 1, 1, &arg)) {
return NULL;
}
Py_ssize_t flags = PyNumber_AsSsize_t(arg, PyExc_OverflowError);
if (flags == -1 && PyErr_Occurred()) {
return NULL;
}
if (flags > INT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"buffer flags too large");
return NULL;
}
return PyMemoryView_FromObjectAndFlags(self, Py_SAFE_DOWNCAST(flags, Py_ssize_t, int));
}
static PyObject *
wrap_releasebuffer(PyObject *self, PyObject *args, void *wrapped)
{
PyObject *arg = NULL;
if (!PyArg_UnpackTuple(args, "", 1, 1, &arg)) {
return NULL;
}
if (!PyMemoryView_Check(arg)) {
PyErr_SetString(PyExc_TypeError,
"expected a memoryview object");
return NULL;
}
PyMemoryViewObject *mview = (PyMemoryViewObject *)arg;
if (mview->view.obj != self) {
PyErr_SetString(PyExc_ValueError,
"memoryview's buffer is not this object");
return NULL;
}
if (mview->flags & _Py_MEMORYVIEW_RELEASED) {
PyErr_SetString(PyExc_ValueError,
"memoryview's buffer has already been released");
return NULL;
}
PyObject *res = PyObject_CallMethodNoArgs((PyObject *)mview, &_Py_ID(release));
if (res == NULL) {
return NULL;
}
Py_DECREF(res);
Py_RETURN_NONE;
}
static PyObject *
wrap_init(PyObject *self, PyObject *args, void *wrapped, PyObject *kwds)
{
@ -8895,6 +8948,132 @@ slot_tp_finalize(PyObject *self)
PyErr_SetRaisedException(exc);
}
typedef struct _PyBufferWrapper {
PyObject_HEAD
PyObject *mv;
PyObject *obj;
} PyBufferWrapper;
static int
bufferwrapper_traverse(PyBufferWrapper *self, visitproc visit, void *arg)
{
Py_VISIT(self->mv);
Py_VISIT(self->obj);
return 0;
}
static void
bufferwrapper_dealloc(PyObject *self)
{
PyBufferWrapper *bw = (PyBufferWrapper *)self;
_PyObject_GC_UNTRACK(self);
Py_XDECREF(bw->mv);
Py_XDECREF(bw->obj);
Py_TYPE(self)->tp_free(self);
}
static void
bufferwrapper_releasebuf(PyObject *self, Py_buffer *view)
{
PyBufferWrapper *bw = (PyBufferWrapper *)self;
assert(PyMemoryView_Check(bw->mv));
Py_TYPE(bw->mv)->tp_as_buffer->bf_releasebuffer(bw->mv, view);
if (Py_TYPE(bw->obj)->tp_as_buffer != NULL
&& Py_TYPE(bw->obj)->tp_as_buffer->bf_releasebuffer != NULL) {
Py_TYPE(bw->obj)->tp_as_buffer->bf_releasebuffer(bw->obj, view);
}
}
static PyBufferProcs bufferwrapper_as_buffer = {
.bf_releasebuffer = bufferwrapper_releasebuf,
};
PyTypeObject _PyBufferWrapper_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "_buffer_wrapper",
.tp_basicsize = sizeof(PyBufferWrapper),
.tp_alloc = PyType_GenericAlloc,
.tp_free = PyObject_GC_Del,
.tp_traverse = (traverseproc)bufferwrapper_traverse,
.tp_dealloc = bufferwrapper_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_as_buffer = &bufferwrapper_as_buffer,
};
static int
slot_bf_getbuffer(PyObject *self, Py_buffer *buffer, int flags)
{
PyObject *flags_obj = PyLong_FromLong(flags);
if (flags_obj == NULL) {
return -1;
}
PyBufferWrapper *wrapper = NULL;
PyObject *stack[2] = {self, flags_obj};
PyObject *ret = vectorcall_method(&_Py_ID(__buffer__), stack, 2);
if (ret == NULL) {
goto fail;
}
if (!PyMemoryView_Check(ret)) {
PyErr_Format(PyExc_TypeError,
"__buffer__ returned non-memoryview object");
goto fail;
}
if (PyObject_GetBuffer(ret, buffer, flags) < 0) {
goto fail;
}
assert(buffer->obj == ret);
wrapper = PyObject_GC_New(PyBufferWrapper, &_PyBufferWrapper_Type);
if (wrapper == NULL) {
goto fail;
}
wrapper->mv = ret;
wrapper->obj = Py_NewRef(self);
_PyObject_GC_TRACK(wrapper);
buffer->obj = (PyObject *)wrapper;
Py_DECREF(ret);
Py_DECREF(flags_obj);
return 0;
fail:
Py_XDECREF(wrapper);
Py_XDECREF(ret);
Py_DECREF(flags_obj);
return -1;
}
static void
slot_bf_releasebuffer(PyObject *self, Py_buffer *buffer)
{
PyObject *mv;
if (Py_TYPE(buffer->obj) == &_PyBufferWrapper_Type) {
// Make sure we pass the same memoryview to
// __release_buffer__() that __buffer__() returned.
mv = Py_NewRef(((PyBufferWrapper *)buffer->obj)->mv);
}
else {
mv = PyMemoryView_FromBuffer(buffer);
if (mv == NULL) {
PyErr_WriteUnraisable(self);
return;
}
}
PyObject *stack[2] = {self, mv};
PyObject *ret = vectorcall_method(&_Py_ID(__release_buffer__), stack, 2);
Py_DECREF(mv);
if (ret == NULL) {
PyErr_WriteUnraisable(self);
}
else {
Py_DECREF(ret);
}
}
static PyObject *
slot_am_await(PyObject *self)
{
@ -8962,6 +9141,7 @@ an all-zero entry.
#undef TPSLOT
#undef FLSLOT
#undef BUFSLOT
#undef AMSLOT
#undef ETSLOT
#undef SQSLOT
@ -8981,6 +9161,8 @@ an all-zero entry.
#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
{#NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \
PyDoc_STR(DOC), .name_strobj = &_Py_ID(NAME) }
#define BUFSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
ETSLOT(NAME, as_buffer.SLOT, FUNCTION, WRAPPER, DOC)
#define AMSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
ETSLOT(NAME, as_async.SLOT, FUNCTION, WRAPPER, DOC)
#define SQSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
@ -9062,6 +9244,13 @@ static pytype_slotdef slotdefs[] = {
"Create and return new object. See help(type) for accurate signature."),
TPSLOT(__del__, tp_finalize, slot_tp_finalize, (wrapperfunc)wrap_del, ""),
BUFSLOT(__buffer__, bf_getbuffer, slot_bf_getbuffer, wrap_buffer,
"__buffer__($self, flags, /)\n--\n\n"
"Return a buffer object that exposes the underlying memory of the object."),
BUFSLOT(__release_buffer__, bf_releasebuffer, slot_bf_releasebuffer, wrap_releasebuffer,
"__release_buffer__($self, /)\n--\n\n"
"Release the buffer object that exposes the underlying memory of the object."),
AMSLOT(__await__, am_await, slot_am_await, wrap_unaryfunc,
"__await__($self, /)\n--\n\nReturn an iterator to be used in await expression."),
AMSLOT(__aiter__, am_aiter, slot_am_aiter, wrap_unaryfunc,
@ -9208,8 +9397,12 @@ slotptr(PyTypeObject *type, int ioffset)
/* Note: this depends on the order of the members of PyHeapTypeObject! */
assert(offset >= 0);
assert((size_t)offset < offsetof(PyHeapTypeObject, as_buffer));
if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {
assert((size_t)offset < offsetof(PyHeapTypeObject, ht_name));
if ((size_t)offset >= offsetof(PyHeapTypeObject, as_buffer)) {
ptr = (char *)type->tp_as_buffer;
offset -= offsetof(PyHeapTypeObject, as_buffer);
}
else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {
ptr = (char *)type->tp_as_sequence;
offset -= offsetof(PyHeapTypeObject, as_sequence);
}