From cdd612ee5b9b577cc886877031e06576cceaf1fe Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 19 Oct 2020 12:26:38 -0700 Subject: [PATCH 1/8] Introduce dedicated entry in PyAsyncMethods for sending values --- Include/abstract.h | 5 ---- Include/cpython/object.h | 13 +++++++++++ Lib/test/test_sys.py | 2 +- Modules/_asynciomodule.c | 48 +++++++++++++++++++++++++++++++-------- Modules/_testcapimodule.c | 3 ++- Objects/genobject.c | 16 +++++++++---- 6 files changed, 65 insertions(+), 22 deletions(-) diff --git a/Include/abstract.h b/Include/abstract.h index 28e576b92935f9..0bd1ca936846fe 100644 --- a/Include/abstract.h +++ b/Include/abstract.h @@ -339,11 +339,6 @@ PyAPI_FUNC(int) PyIter_Check(PyObject *); PyAPI_FUNC(PyObject *) PyIter_Next(PyObject *); #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 -typedef enum { - PYGEN_RETURN = 0, - PYGEN_ERROR = -1, - PYGEN_NEXT = 1, -} PySendResult; /* Takes generator, coroutine or iterator object and sends the value into it. Returns: diff --git a/Include/cpython/object.h b/Include/cpython/object.h index 875a600f79565a..daa69fec913154 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -167,10 +167,23 @@ typedef struct { objobjargproc mp_ass_subscript; } PyMappingMethods; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 +typedef enum { + PYGEN_RETURN = 0, + PYGEN_ERROR = -1, + PYGEN_NEXT = 1, +} PySendResult; + +typedef PySendResult (*sendfunc)(PyObject *iter, PyObject *value, PyObject **result); +#endif + typedef struct { unaryfunc am_await; unaryfunc am_aiter; unaryfunc am_anext; +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 + sendfunc am_send; +#endif } PyAsyncMethods; typedef struct { diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 30c29a26a99270..bb567088425852 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1340,7 +1340,7 @@ def delx(self): del self.__x check(int, s) # class s = vsize(fmt + # PyTypeObject - '3P' # PyAsyncMethods + '4P' # PyAsyncMethods '36P' # PyNumberMethods '3P' # PyMappingMethods '10P' # PySequenceMethods diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index f01e5884c6fe20..a71a976ecd4fb2 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1484,7 +1484,8 @@ future_cls_getitem(PyObject *cls, PyObject *type) static PyAsyncMethods FutureType_as_async = { (unaryfunc)future_new_iter, /* am_await */ 0, /* am_aiter */ - 0 /* am_anext */ + 0, /* am_anext */ + 0, /* am_send */ }; static PyMethodDef FutureType_methods[] = { @@ -1602,37 +1603,55 @@ FutureIter_dealloc(futureiterobject *it) } } -static PyObject * -FutureIter_iternext(futureiterobject *it) +static PySendResult +FutureIter_am_send(futureiterobject *it, PyObject *arg, PyObject **result) { PyObject *res; FutureObj *fut = it->future; if (fut == NULL) { - return NULL; + return PYGEN_ERROR; } if (fut->fut_state == STATE_PENDING) { if (!fut->fut_blocking) { fut->fut_blocking = 1; Py_INCREF(fut); - return (PyObject *)fut; + *result = (PyObject *)fut; + return PYGEN_NEXT; } PyErr_SetString(PyExc_RuntimeError, "await wasn't used with future"); - return NULL; + return PYGEN_ERROR; } it->future = NULL; res = _asyncio_Future_result_impl(fut); if (res != NULL) { - /* The result of the Future is not an exception. */ - (void)_PyGen_SetStopIterationValue(res); - Py_DECREF(res); + *result = res; + return PYGEN_RETURN; } Py_DECREF(fut); - return NULL; + return PYGEN_ERROR; +} + +static PyObject * +FutureIter_iternext(futureiterobject *it) +{ + PyObject *result; + switch (FutureIter_am_send(it, Py_None, &result)) { + case PYGEN_RETURN: + (void)_PyGen_SetStopIterationValue(result); + Py_DECREF(result); + return NULL; + case PYGEN_NEXT: + return result; + case PYGEN_ERROR: + return NULL; + default: + Py_UNREACHABLE(); + } } static PyObject * @@ -1721,12 +1740,21 @@ static PyMethodDef FutureIter_methods[] = { {NULL, NULL} /* Sentinel */ }; +static PyAsyncMethods FutureIterType_as_async = { + 0, /* am_await */ + 0, /* am_aiter */ + 0, /* am_anext */ + (sendfunc)FutureIter_am_send, /* am_send */ +}; + + static PyTypeObject FutureIterType = { PyVarObject_HEAD_INIT(NULL, 0) "_asyncio.FutureIter", .tp_basicsize = sizeof(futureiterobject), .tp_itemsize = 0, .tp_dealloc = (destructor)FutureIter_dealloc, + .tp_as_async = &FutureIterType_as_async, .tp_getattro = PyObject_GenericGetAttr, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_traverse = (traverseproc)FutureIter_traverse, diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 28d2c124d51775..ed25a6d6854b47 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -6142,7 +6142,8 @@ awaitObject_await(awaitObject *ao) static PyAsyncMethods awaitType_as_async = { (unaryfunc)awaitObject_await, /* am_await */ 0, /* am_aiter */ - 0 /* am_anext */ + 0, /* am_anext */ + 0, /* am_send */ }; diff --git a/Objects/genobject.c b/Objects/genobject.c index c1b26e9da33bea..cef6f0f61c66ea 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -274,7 +274,9 @@ PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) _Py_IDENTIFIER(send); assert(arg != NULL); assert(result != NULL); - + if (Py_TYPE(iter)->tp_as_async != NULL && Py_TYPE(iter)->tp_as_async->am_send != NULL) { + return Py_TYPE(iter)->tp_as_async->am_send(iter, arg, result); + } if (PyGen_CheckExact(iter) || PyCoro_CheckExact(iter)) { return gen_send_ex2((PyGenObject *)iter, arg, result, 0, 0); } @@ -1031,7 +1033,8 @@ static PyMethodDef coro_methods[] = { static PyAsyncMethods coro_as_async = { (unaryfunc)coro_await, /* am_await */ 0, /* am_aiter */ - 0 /* am_anext */ + 0, /* am_anext */ + 0, /* am_send */ }; PyTypeObject PyCoro_Type = { @@ -1413,7 +1416,8 @@ static PyMethodDef async_gen_methods[] = { static PyAsyncMethods async_gen_as_async = { 0, /* am_await */ PyObject_SelfIter, /* am_aiter */ - (unaryfunc)async_gen_anext /* am_anext */ + (unaryfunc)async_gen_anext, /* am_anext */ + 0, /* am_send */ }; @@ -1676,7 +1680,8 @@ static PyMethodDef async_gen_asend_methods[] = { static PyAsyncMethods async_gen_asend_as_async = { PyObject_SelfIter, /* am_await */ 0, /* am_aiter */ - 0 /* am_anext */ + 0, /* am_anext */ + 0, /* am_send */ }; @@ -2084,7 +2089,8 @@ static PyMethodDef async_gen_athrow_methods[] = { static PyAsyncMethods async_gen_athrow_as_async = { PyObject_SelfIter, /* am_await */ 0, /* am_aiter */ - 0 /* am_anext */ + 0, /* am_anext */ + 0, /* am_send */ }; From 7fb80fd04d3845ba3c7f48e717bbede6c38e38fe Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 19 Oct 2020 13:32:48 -0700 Subject: [PATCH 2/8] PR feedback --- Include/cpython/object.h | 10 ---------- Include/object.h | 9 +++++++++ Modules/_asynciomodule.c | 5 ++++- Objects/abstract.c | 24 ++++++++++++++++++++++++ Objects/genobject.c | 32 +++++--------------------------- 5 files changed, 42 insertions(+), 38 deletions(-) diff --git a/Include/cpython/object.h b/Include/cpython/object.h index daa69fec913154..b104aed069ffcd 100644 --- a/Include/cpython/object.h +++ b/Include/cpython/object.h @@ -167,23 +167,13 @@ typedef struct { objobjargproc mp_ass_subscript; } PyMappingMethods; -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 -typedef enum { - PYGEN_RETURN = 0, - PYGEN_ERROR = -1, - PYGEN_NEXT = 1, -} PySendResult; - typedef PySendResult (*sendfunc)(PyObject *iter, PyObject *value, PyObject **result); -#endif typedef struct { unaryfunc am_await; unaryfunc am_aiter; unaryfunc am_anext; -#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 sendfunc am_send; -#endif } PyAsyncMethods; typedef struct { diff --git a/Include/object.h b/Include/object.h index 6ee4ee7848551e..83554ab8cc35c0 100644 --- a/Include/object.h +++ b/Include/object.h @@ -557,6 +557,15 @@ PyAPI_DATA(PyObject) _Py_NotImplementedStruct; /* Don't use this directly */ #define Py_GT 4 #define Py_GE 5 +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 +/* Result of calling PyIter_Send */ +typedef enum { + PYGEN_RETURN = 0, + PYGEN_ERROR = -1, + PYGEN_NEXT = 1, +} PySendResult; +#endif + /* * Macro for implementing rich comparisons * diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index a71a976ecd4fb2..3b2409351988db 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1604,11 +1604,14 @@ FutureIter_dealloc(futureiterobject *it) } static PySendResult -FutureIter_am_send(futureiterobject *it, PyObject *arg, PyObject **result) +FutureIter_am_send(futureiterobject *it, PyObject *Py_UNUSED(arg), PyObject **result) { + /* arg is unused, see the comment on FutureIter_send for clarification */ + PyObject *res; FutureObj *fut = it->future; + *result = NULL; if (fut == NULL) { return PYGEN_ERROR; } diff --git a/Objects/abstract.c b/Objects/abstract.c index 562549876beed8..ddf2bf28d90d3e 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2669,6 +2669,30 @@ PyIter_Next(PyObject *iter) return result; } +PySendResult +PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) +{ + _Py_IDENTIFIER(send); + assert(arg != NULL); + assert(result != NULL); + if (Py_TYPE(iter)->tp_as_async != NULL && Py_TYPE(iter)->tp_as_async->am_send != NULL) { + return Py_TYPE(iter)->tp_as_async->am_send(iter, arg, result); + } + if (arg == Py_None && PyIter_Check(iter)) { + *result = Py_TYPE(iter)->tp_iternext(iter); + } + else { + *result = _PyObject_CallMethodIdOneArg(iter, &PyId_send, arg); + } + if (*result != NULL) { + return PYGEN_NEXT; + } + if (_PyGen_FetchStopIterationValue(result) == 0) { + return PYGEN_RETURN; + } + return PYGEN_ERROR; +} + /* * Flatten a sequence of bytes() objects into a C array of * NULL terminated string pointers with a NULL char* terminating the array. diff --git a/Objects/genobject.c b/Objects/genobject.c index cef6f0f61c66ea..c160aaa4500ee1 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -268,32 +268,10 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, return result ? PYGEN_RETURN : PYGEN_ERROR; } -PySendResult -PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) +static PySendResult +PyGen_am_send(PyGenObject *gen, PyObject *arg, PyObject **result) { - _Py_IDENTIFIER(send); - assert(arg != NULL); - assert(result != NULL); - if (Py_TYPE(iter)->tp_as_async != NULL && Py_TYPE(iter)->tp_as_async->am_send != NULL) { - return Py_TYPE(iter)->tp_as_async->am_send(iter, arg, result); - } - if (PyGen_CheckExact(iter) || PyCoro_CheckExact(iter)) { - return gen_send_ex2((PyGenObject *)iter, arg, result, 0, 0); - } - - if (arg == Py_None && PyIter_Check(iter)) { - *result = Py_TYPE(iter)->tp_iternext(iter); - } - else { - *result = _PyObject_CallMethodIdOneArg(iter, &PyId_send, arg); - } - if (*result != NULL) { - return PYGEN_NEXT; - } - if (_PyGen_FetchStopIterationValue(result) == 0) { - return PYGEN_RETURN; - } - return PYGEN_ERROR; + return gen_send_ex2(gen, arg, result, 0, 0); } static PyObject * @@ -1034,7 +1012,7 @@ static PyAsyncMethods coro_as_async = { (unaryfunc)coro_await, /* am_await */ 0, /* am_aiter */ 0, /* am_anext */ - 0, /* am_send */ + (sendfunc)PyGen_am_send, /* am_send */ }; PyTypeObject PyCoro_Type = { @@ -1417,7 +1395,7 @@ static PyAsyncMethods async_gen_as_async = { 0, /* am_await */ PyObject_SelfIter, /* am_aiter */ (unaryfunc)async_gen_anext, /* am_anext */ - 0, /* am_send */ + (sendfunc)PyGen_am_send, /* am_send */ }; From d11ba245b6ba810e387a07a6bec5934def0e57ca Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Mon, 19 Oct 2020 15:58:29 -0700 Subject: [PATCH 3/8] Added NEWS --- Misc/NEWS.d/next/C API/2020-10-19-15-58-16.bpo-42085.NhEf3W.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/C API/2020-10-19-15-58-16.bpo-42085.NhEf3W.rst diff --git a/Misc/NEWS.d/next/C API/2020-10-19-15-58-16.bpo-42085.NhEf3W.rst b/Misc/NEWS.d/next/C API/2020-10-19-15-58-16.bpo-42085.NhEf3W.rst new file mode 100644 index 00000000000000..53338fb4f446e1 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2020-10-19-15-58-16.bpo-42085.NhEf3W.rst @@ -0,0 +1 @@ +Add dedicated entry to PyAsyncMethods for sending values From 4635bf147e08a01309296ef9be17c7483e88d687 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 20 Oct 2020 12:56:39 -0700 Subject: [PATCH 4/8] Added am_send for generators --- Objects/genobject.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Objects/genobject.c b/Objects/genobject.c index c160aaa4500ee1..cc33b00aa17b48 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -768,6 +768,14 @@ static PyMethodDef gen_methods[] = { {NULL, NULL} /* Sentinel */ }; +static PyAsyncMethods gen_as_async = { + 0, /* am_await */ + 0, /* am_aiter */ + 0, /* am_anext */ + (sendfunc)PyGen_am_send, /* am_send */ +}; + + PyTypeObject PyGen_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) "generator", /* tp_name */ @@ -778,7 +786,7 @@ PyTypeObject PyGen_Type = { 0, /* tp_vectorcall_offset */ 0, /* tp_getattr */ 0, /* tp_setattr */ - 0, /* tp_as_async */ + &gen_as_async, /* tp_as_async */ (reprfunc)gen_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ From 6559a1eabcaefd49cb8dec66cb2ccc800ce69ef2 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 20 Oct 2020 15:59:34 -0700 Subject: [PATCH 5/8] Added Py_TPFLAGS_HAVE_SEND flag --- Include/object.h | 3 +++ Include/typeslots.h | 4 ++++ Modules/_asynciomodule.c | 2 +- Objects/abstract.c | 4 +++- Objects/genobject.c | 9 ++++++--- Objects/typeobject.c | 7 +++++++ 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/Include/object.h b/Include/object.h index 83554ab8cc35c0..f203263502defb 100644 --- a/Include/object.h +++ b/Include/object.h @@ -356,6 +356,9 @@ given type object has a specified feature. /* Type is abstract and cannot be instantiated */ #define Py_TPFLAGS_IS_ABSTRACT (1UL << 20) +/* Type has am_send entry in tp_as_async slot */ +#define Py_TPFLAGS_HAVE_SEND (1UL << 21) + /* These flags are used to determine if a type is a subclass. */ #define Py_TPFLAGS_LONG_SUBCLASS (1UL << 24) #define Py_TPFLAGS_LIST_SUBCLASS (1UL << 25) diff --git a/Include/typeslots.h b/Include/typeslots.h index 64f6fff5144493..5800d0158bc924 100644 --- a/Include/typeslots.h +++ b/Include/typeslots.h @@ -88,3 +88,7 @@ /* New in 3.5 */ #define Py_tp_finalize 80 #endif +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 +/* New in 3.10 */ +#define Py_am_send 81 +#endif diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index 3b2409351988db..e78a7873c0c0e1 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1759,7 +1759,7 @@ static PyTypeObject FutureIterType = { .tp_dealloc = (destructor)FutureIter_dealloc, .tp_as_async = &FutureIterType_as_async, .tp_getattro = PyObject_GenericGetAttr, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_SEND, .tp_traverse = (traverseproc)FutureIter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)FutureIter_iternext, diff --git a/Objects/abstract.c b/Objects/abstract.c index ddf2bf28d90d3e..ba5555aed576ab 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2675,7 +2675,9 @@ PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) _Py_IDENTIFIER(send); assert(arg != NULL); assert(result != NULL); - if (Py_TYPE(iter)->tp_as_async != NULL && Py_TYPE(iter)->tp_as_async->am_send != NULL) { + if (PyType_HasFeature(Py_TYPE(iter), Py_TPFLAGS_HAVE_SEND)) { + assert (Py_TYPE(iter)->tp_as_async != NULL); + assert (Py_TYPE(iter)->tp_as_async->am_send != NULL); return Py_TYPE(iter)->tp_as_async->am_send(iter, arg, result); } if (arg == Py_None && PyIter_Check(iter)) { diff --git a/Objects/genobject.c b/Objects/genobject.c index cc33b00aa17b48..bf79852f4fe7ec 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -797,7 +797,8 @@ PyTypeObject PyGen_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_HAVE_SEND, /* tp_flags */ 0, /* tp_doc */ (traverseproc)gen_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -1044,7 +1045,8 @@ PyTypeObject PyCoro_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_HAVE_SEND, /* tp_flags */ 0, /* tp_doc */ (traverseproc)gen_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -1428,7 +1430,8 @@ PyTypeObject PyAsyncGen_Type = { PyObject_GenericGetAttr, /* tp_getattro */ 0, /* tp_setattro */ 0, /* tp_as_buffer */ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_HAVE_SEND, /* tp_flags */ 0, /* tp_doc */ (traverseproc)async_gen_traverse, /* tp_traverse */ 0, /* tp_clear */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index 36c7662e081a40..aafc05c048f0c6 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5409,6 +5409,13 @@ PyType_Ready(PyTypeObject *type) _PyObject_ASSERT((PyObject *)type, type->tp_vectorcall_offset > 0); _PyObject_ASSERT((PyObject *)type, type->tp_call != NULL); } + /* Consistency check for Py_TPFLAGS_HAVE_SEND - flag requires + * type->tp_as_async->am_send to be present. + */ + if (type->tp_flags & Py_TPFLAGS_HAVE_SEND) { + _PyObject_ASSERT((PyObject *)type, type->tp_as_async != NULL); + _PyObject_ASSERT((PyObject *)type, type->tp_as_async->am_send != NULL); + } type->tp_flags |= Py_TPFLAGS_READYING; From 0269d6c50d4084d8cd25073762aa2af4e096c65a Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 20 Oct 2020 16:11:20 -0700 Subject: [PATCH 6/8] add if-def around Py_TPFLAGS_HAVE_SEND --- Include/object.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Include/object.h b/Include/object.h index f203263502defb..02477ec285926d 100644 --- a/Include/object.h +++ b/Include/object.h @@ -356,8 +356,10 @@ given type object has a specified feature. /* Type is abstract and cannot be instantiated */ #define Py_TPFLAGS_IS_ABSTRACT (1UL << 20) +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 /* Type has am_send entry in tp_as_async slot */ #define Py_TPFLAGS_HAVE_SEND (1UL << 21) +#endif /* These flags are used to determine if a type is a subclass. */ #define Py_TPFLAGS_LONG_SUBCLASS (1UL << 24) From 5006b27b294b0674dc109ae7130e992636414598 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 20 Oct 2020 21:02:33 -0700 Subject: [PATCH 7/8] Updated docs --- Doc/c-api/typeobj.rst | 16 ++++++++++++++++ Include/object.h | 2 +- Modules/_asynciomodule.c | 7 +++++-- Objects/abstract.c | 2 +- Objects/genobject.c | 6 +++--- Objects/typeobject.c | 4 ++-- Objects/typeslots.inc | 1 + 7 files changed, 29 insertions(+), 9 deletions(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index ddcb8ae3d0950c..acf2435f1a90bc 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -199,6 +199,8 @@ sub-slots +---------------------------------------------------------+-----------------------------------+--------------+ | :c:member:`~PyAsyncMethods.am_anext` | :c:type:`unaryfunc` | __anext__ | +---------------------------------------------------------+-----------------------------------+--------------+ + | :c:member:`~PyAsyncMethods.am_send` | :c:type:`sendfunc` | | + +---------------------------------------------------------+-----------------------------------+--------------+ | | +---------------------------------------------------------+-----------------------------------+--------------+ | :c:member:`~PyNumberMethods.nb_add` | :c:type:`binaryfunc` | __add__ | @@ -2303,6 +2305,7 @@ Async Object Structures unaryfunc am_await; unaryfunc am_aiter; unaryfunc am_anext; + sendfunc am_send; } PyAsyncMethods; .. c:member:: unaryfunc PyAsyncMethods.am_await @@ -2336,6 +2339,15 @@ Async Object Structures Must return an :term:`awaitable` object. See :meth:`__anext__` for details. This slot may be set to ``NULL``. +.. c:member:: sendfunc PyAsyncMethods.am_send + + The signature of this function is:: + + PySendResult am_send(PyObject *self, PyObject *arg, PyObject **result); + + See :c:func:`PyIter_Send` for details. + This slot may be set to ``NULL``. + .. _slot-typedefs: @@ -2431,6 +2443,10 @@ Slot Type typedefs .. c:type:: PyObject *(*binaryfunc)(PyObject *, PyObject *) +.. c:type:: PySendResult *(*sendfunc)(PyObject *, PyObject *, PyObject **) + + See :c:member:`~PyAsyncMethods.am_send`. + .. c:type:: PyObject *(*ternaryfunc)(PyObject *, PyObject *, PyObject *) .. c:type:: PyObject *(*ssizeargfunc)(PyObject *, Py_ssize_t) diff --git a/Include/object.h b/Include/object.h index 02477ec285926d..e961a07e2f0f6b 100644 --- a/Include/object.h +++ b/Include/object.h @@ -358,7 +358,7 @@ given type object has a specified feature. #if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030A0000 /* Type has am_send entry in tp_as_async slot */ -#define Py_TPFLAGS_HAVE_SEND (1UL << 21) +#define Py_TPFLAGS_HAVE_AM_SEND (1UL << 21) #endif /* These flags are used to determine if a type is a subclass. */ diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c index e78a7873c0c0e1..a34838efd66738 100644 --- a/Modules/_asynciomodule.c +++ b/Modules/_asynciomodule.c @@ -1604,7 +1604,9 @@ FutureIter_dealloc(futureiterobject *it) } static PySendResult -FutureIter_am_send(futureiterobject *it, PyObject *Py_UNUSED(arg), PyObject **result) +FutureIter_am_send(futureiterobject *it, + PyObject *Py_UNUSED(arg), + PyObject **result) { /* arg is unused, see the comment on FutureIter_send for clarification */ @@ -1759,7 +1761,8 @@ static PyTypeObject FutureIterType = { .tp_dealloc = (destructor)FutureIter_dealloc, .tp_as_async = &FutureIterType_as_async, .tp_getattro = PyObject_GenericGetAttr, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_HAVE_SEND, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | + Py_TPFLAGS_HAVE_AM_SEND, .tp_traverse = (traverseproc)FutureIter_traverse, .tp_iter = PyObject_SelfIter, .tp_iternext = (iternextfunc)FutureIter_iternext, diff --git a/Objects/abstract.c b/Objects/abstract.c index ba5555aed576ab..44ed5b3932bf21 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -2675,7 +2675,7 @@ PyIter_Send(PyObject *iter, PyObject *arg, PyObject **result) _Py_IDENTIFIER(send); assert(arg != NULL); assert(result != NULL); - if (PyType_HasFeature(Py_TYPE(iter), Py_TPFLAGS_HAVE_SEND)) { + if (PyType_HasFeature(Py_TYPE(iter), Py_TPFLAGS_HAVE_AM_SEND)) { assert (Py_TYPE(iter)->tp_as_async != NULL); assert (Py_TYPE(iter)->tp_as_async->am_send != NULL); return Py_TYPE(iter)->tp_as_async->am_send(iter, arg, result); diff --git a/Objects/genobject.c b/Objects/genobject.c index bf79852f4fe7ec..bde92b462da199 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -798,7 +798,7 @@ PyTypeObject PyGen_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_HAVE_SEND, /* tp_flags */ + Py_TPFLAGS_HAVE_AM_SEND, /* tp_flags */ 0, /* tp_doc */ (traverseproc)gen_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -1046,7 +1046,7 @@ PyTypeObject PyCoro_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_HAVE_SEND, /* tp_flags */ + Py_TPFLAGS_HAVE_AM_SEND, /* tp_flags */ 0, /* tp_doc */ (traverseproc)gen_traverse, /* tp_traverse */ 0, /* tp_clear */ @@ -1431,7 +1431,7 @@ PyTypeObject PyAsyncGen_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | - Py_TPFLAGS_HAVE_SEND, /* tp_flags */ + Py_TPFLAGS_HAVE_AM_SEND, /* tp_flags */ 0, /* tp_doc */ (traverseproc)async_gen_traverse, /* tp_traverse */ 0, /* tp_clear */ diff --git a/Objects/typeobject.c b/Objects/typeobject.c index aafc05c048f0c6..a37c6b0712b241 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -5409,10 +5409,10 @@ PyType_Ready(PyTypeObject *type) _PyObject_ASSERT((PyObject *)type, type->tp_vectorcall_offset > 0); _PyObject_ASSERT((PyObject *)type, type->tp_call != NULL); } - /* Consistency check for Py_TPFLAGS_HAVE_SEND - flag requires + /* Consistency check for Py_TPFLAGS_HAVE_AM_SEND - flag requires * type->tp_as_async->am_send to be present. */ - if (type->tp_flags & Py_TPFLAGS_HAVE_SEND) { + if (type->tp_flags & Py_TPFLAGS_HAVE_AM_SEND) { _PyObject_ASSERT((PyObject *)type, type->tp_as_async != NULL); _PyObject_ASSERT((PyObject *)type, type->tp_as_async->am_send != NULL); } diff --git a/Objects/typeslots.inc b/Objects/typeslots.inc index ffc9bb2e1c7710..cc4ef1170fd28e 100644 --- a/Objects/typeslots.inc +++ b/Objects/typeslots.inc @@ -79,3 +79,4 @@ offsetof(PyHeapTypeObject, as_async.am_await), offsetof(PyHeapTypeObject, as_async.am_aiter), offsetof(PyHeapTypeObject, as_async.am_anext), offsetof(PyHeapTypeObject, ht_type.tp_finalize), +offsetof(PyHeapTypeObject, as_async.am_send), From 016f3e9c31955d747120e32d16c7e80fd1267e42 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Tue, 20 Oct 2020 21:06:17 -0700 Subject: [PATCH 8/8] fix typo in the signature of sendfunc --- Doc/c-api/typeobj.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index acf2435f1a90bc..235e19f590eaa0 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2443,7 +2443,7 @@ Slot Type typedefs .. c:type:: PyObject *(*binaryfunc)(PyObject *, PyObject *) -.. c:type:: PySendResult *(*sendfunc)(PyObject *, PyObject *, PyObject **) +.. c:type:: PySendResult (*sendfunc)(PyObject *, PyObject *, PyObject **) See :c:member:`~PyAsyncMethods.am_send`.