[Python-checkins] bpo-42085: Introduce dedicated entry in PyAsyncMethods for sending values (#22780)

1st1 webhook-mailer at python.org
Tue Nov 10 15:10:04 EST 2020


https://github.com/python/cpython/commit/1e996c3a3b51e9c6f1f4cea8a6dbcf3bcb865060
commit: 1e996c3a3b51e9c6f1f4cea8a6dbcf3bcb865060
branch: master
author: Vladimir Matveev <vladima at fb.com>
committer: 1st1 <yury at edgedb.com>
date: 2020年11月10日T12:09:55-08:00
summary:
bpo-42085: Introduce dedicated entry in PyAsyncMethods for sending values (#22780)
files:
A Misc/NEWS.d/next/C API/2020-10-19-15-58-16.bpo-42085.NhEf3W.rst
M Doc/c-api/typeobj.rst
M Include/abstract.h
M Include/cpython/object.h
M Include/object.h
M Include/typeslots.h
M Lib/test/test_sys.py
M Modules/_asynciomodule.c
M Modules/_testcapimodule.c
M Objects/abstract.c
M Objects/genobject.c
M Objects/typeobject.c
M Objects/typeslots.inc
diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst
index 32bbc7ba0a168..6a67bfe901089 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__ |
@@ -2304,6 +2306,7 @@ Async Object Structures
 unaryfunc am_await;
 unaryfunc am_aiter;
 unaryfunc am_anext;
+ sendfunc am_send;
 } PyAsyncMethods;
 
 .. c:member:: unaryfunc PyAsyncMethods.am_await
@@ -2337,6 +2340,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:
 
@@ -2432,6 +2444,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/abstract.h b/Include/abstract.h
index 28e576b92935f..0bd1ca936846f 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 0db53c312f07b..ec6a364767776 100644
--- a/Include/cpython/object.h
+++ b/Include/cpython/object.h
@@ -167,10 +167,13 @@ typedef struct {
 objobjargproc mp_ass_subscript;
 } PyMappingMethods;
 
+typedef PySendResult (*sendfunc)(PyObject *iter, PyObject *value, PyObject **result);
+
 typedef struct {
 unaryfunc am_await;
 unaryfunc am_aiter;
 unaryfunc am_anext;
+ sendfunc am_send;
 } PyAsyncMethods;
 
 typedef struct {
diff --git a/Include/object.h b/Include/object.h
index eab3228f3abe4..dd1b217686717 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -356,6 +356,11 @@ 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_AM_SEND (1UL << 21)
+#endif
+
 /* 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)
@@ -582,6 +587,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/Include/typeslots.h b/Include/typeslots.h
index 64f6fff514449..5800d0158bc92 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/Lib/test/test_sys.py b/Lib/test/test_sys.py
index 332ed8f550c98..173ef9ebb4c19 100644
--- a/Lib/test/test_sys.py
+++ b/Lib/test/test_sys.py
@@ -1407,7 +1407,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/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 0000000000000..53338fb4f446e
--- /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
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 90d288f739393..d1d0f6bc75e42 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -1479,7 +1479,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[] = {
@@ -1597,37 +1598,60 @@ FutureIter_dealloc(futureiterobject *it)
 }
 }
 
-static PyObject *
-FutureIter_iternext(futureiterobject *it)
+static PySendResult
+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 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 *
@@ -1716,14 +1740,24 @@ 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_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/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 22d20d220d408..4382b642dca7f 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/abstract.c b/Objects/abstract.c
index 562549876beed..44ed5b3932bf2 100644
--- a/Objects/abstract.c
+++ b/Objects/abstract.c
@@ -2669,6 +2669,32 @@ 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 (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);
+ }
+ 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 c1b26e9da33be..bde92b462da19 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -268,30 +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 (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 *
@@ -788,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 */
@@ -798,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 */
@@ -809,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_AM_SEND, /* tp_flags */
 0, /* tp_doc */
 (traverseproc)gen_traverse, /* tp_traverse */
 0, /* tp_clear */
@@ -1031,7 +1020,8 @@ static PyMethodDef coro_methods[] = {
 static PyAsyncMethods coro_as_async = {
 (unaryfunc)coro_await, /* am_await */
 0, /* am_aiter */
- 0 /* am_anext */
+ 0, /* am_anext */
+ (sendfunc)PyGen_am_send, /* am_send */
 };
 
 PyTypeObject PyCoro_Type = {
@@ -1055,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_AM_SEND, /* tp_flags */
 0, /* tp_doc */
 (traverseproc)gen_traverse, /* tp_traverse */
 0, /* tp_clear */
@@ -1413,7 +1404,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 */
+ (sendfunc)PyGen_am_send, /* am_send */
 };
 
 
@@ -1438,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_AM_SEND, /* tp_flags */
 0, /* tp_doc */
 (traverseproc)async_gen_traverse, /* tp_traverse */
 0, /* tp_clear */
@@ -1676,7 +1669,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 +2078,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 */
 };
 
 
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 55bf9b3f38927..b4188b8bcaf04 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -5427,6 +5427,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_AM_SEND - flag requires
+ * type->tp_as_async->am_send to be present.
+ */
+ 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);
+ }
 
 type->tp_flags |= Py_TPFLAGS_READYING;
 
diff --git a/Objects/typeslots.inc b/Objects/typeslots.inc
index ffc9bb2e1c771..cc4ef1170fd28 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),


More information about the Python-checkins mailing list

AltStyle によって変換されたページ (->オリジナル) /