8000 bpo-42085: Introduce dedicated entry in PyAsyncMethods for sending values by vladima · Pull Request #22780 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-42085: Introduce dedicated entry in PyAsyncMethods for sending values #22780

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Nov 10, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions Include/abstract.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not we add corresponding Py_TPFLAGS_HAVE_ flag for binary compatibility?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typeslots.h should be updated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not we add corresponding Py_TPFLAGS_HAVE_ flag for binary compatibility?

Do we support extensions compiled for higher versions of Python in lower versions of Python? If I compile something for 3.10 I wouldn't expect it to work in 3.9.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we don't need a Py_TPFLAGS_HAVE_ flag and I don't remember adding one for the as_async slot when we implemented PEP 492.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But if you compile something for 3.9 it is expected to work in 3.10 and do not read uninitialized slot.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps there is a bug in the PEP 492 implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, I see now, thanks for explaining! Yeah, in this case we do need a flag.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps there is a bug in the PEP 492 implementation.

Well, I don't think we need to do anything now that as_async has been there since 3.5. But looking back, maybe we should have added a flag.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We used an existing slot for PEP-492, so the ABI did not change.
I don't think we necessarily need a new flag. See https://bugs.python.org/issue32388

} PyAsyncMethods;

typedef struct {
Expand Down
14 changes: 14 additions & 0 deletions Include/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_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)
Expand Down Expand Up @@ -557,6 +562,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
*
Expand Down
4 changes: 4 additions & 0 deletions Include/typeslots.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 1 addition & 1 deletion Lib/test/test_sys.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add dedicated entry to PyAsyncMethods for sending values
53 changes: 42 additions & 11 deletions Modules/_asynciomodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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[] = {
Expand Down Expand Up @@ -1602,37 +1603,58 @@ FutureIter_dealloc(futureiterobject *it)
}
}

static PyObject *
FutureIter_iternext(futureiterobject *it)
static PySendResult
FutureIter_am_send(futureiterobject *it, PyObject *Py_UNUSED(arg), PyObject **result)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to keep lines under 79 characters

{
/* 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 *
Expand Down Expand Up @@ -1721,14 +1743,23 @@ 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_SEND,
.tp_traverse = (traverseproc)FutureIter_traverse,
.tp_iter = PyObject_SelfIter,
.tp_iternext = (iternextfunc)FutureIter_iternext,
Expand Down
3 changes: 2 additions & 1 deletion Modules/_testcapimodule.c