]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
bpo-42085: Introduce dedicated entry in PyAsyncMethods for sending values (#22780)
authorVladimir Matveev <vladima@fb.com>
Tue, 10 Nov 2020 20:09:55 +0000 (12:09 -0800)
committerGitHub <noreply@github.com>
Tue, 10 Nov 2020 20:09:55 +0000 (12:09 -0800)
13 files changed:
Doc/c-api/typeobj.rst
Include/abstract.h
Include/cpython/object.h
Include/object.h
Include/typeslots.h
Lib/test/test_sys.py
Misc/NEWS.d/next/C API/2020-10-19-15-58-16.bpo-42085.NhEf3W.rst [new file with mode: 0644]
Modules/_asynciomodule.c
Modules/_testcapimodule.c
Objects/abstract.c
Objects/genobject.c
Objects/typeobject.c
Objects/typeslots.inc

index 32bbc7ba0a168d4d25fe6862ddf4ccf612f2f054..6a67bfe90108901244b9427d65afeb8a10fbd493 100644 (file)
@@ -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)
index 28e576b92935f9e92f0e86347d43bcbfac1afe55..0bd1ca936846feb3e71f8c7ef42ce4079dc5cd0a 100644 (file)
@@ -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:
index 0db53c312f07b744376beb0cd4b76b0c946355a3..ec6a3647677766c495d2a937a628ff39628aadf7 100644 (file)
@@ -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 {
index eab3228f3abe41425e621de0e1087be8047b789e..dd1b217686717e2387d2c10b2ba80253402b7bd9 100644 (file)
@@ -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
  *
index 64f6fff51444937d70c3d2af414af0bfa040cbee..5800d0158bc924374d90c1556de2790573d95a2d 100644 (file)
@@ -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
index 332ed8f550c984351d4799e72332eff3e6541d70..173ef9ebb4c19535cb0d36cf5b80a7cf2c8c215f 100644 (file)
@@ -1407,7 +1407,7 @@ class SizeofTest(unittest.TestCase):
         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 (file)
index 0000000..53338fb
--- /dev/null
@@ -0,0 +1 @@
+Add dedicated entry to PyAsyncMethods for sending values
index 90d288f739393482cd86bf93487a9d494e8fc2de..d1d0f6bc75e4248ca3ca9653dacc8879175e8cd2 100644 (file)
@@ -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,
index 22d20d220d408920274ce40d9d178ccb19c41cd8..4382b642dca7fb040dad53c7fca41156e438eab1 100644 (file)
@@ -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  */
 };
 
 
index 562549876beed82399210d3dd9f7598dd7df6e71..44ed5b3932bf21658028fd3082639b3165d8d126 100644 (file)
@@ -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.
index c1b26e9da33bea467d9563d3c29c06ad8b4c3bbc..bde92b462da19978e7d049382cc277f44f90745f 100644 (file)
@@ -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  */
 };
 
 
index 55bf9b3f38927362031b8a2474ffdf4915fe9ecf..b4188b8bcaf045829cbb19096dbaa71e417cd9cb 100644 (file)
@@ -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;
 
index ffc9bb2e1c7710bc3ed96ccd4183a8bc5f910048..cc4ef1170fd28e7d0cf0f811fc1c2ae0fb29e668 100644 (file)
@@ -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),