https://github.com/python/cpython/commit/312c427fe4169c5bee10b399667f155d398d8184
commit: 312c427fe4169c5bee10b399667f155d398d8184
branch: main
author: Michael J. Sullivan <[email protected]>
committer: kumaraditya303 <[email protected]>
date: 2026-10-28T13:47:42+05:30
summary:

gh-158192: expose the future in asyncio FutureIter as `fi_future` (#158470)

files:
A Misc/NEWS.d/next/Library/2026-09-29-19-34-30.gh-issue-158192.6wPjiU.rst
M Lib/asyncio/futures.py
M Lib/test/test_asyncio/test_futures.py
M Modules/_asynciomodule.c

diff --git a/Lib/asyncio/futures.py b/Lib/asyncio/futures.py
index 11858a0274a69f..76675988c2bfe2 100644
--- a/Lib/asyncio/futures.py
+++ b/Lib/asyncio/futures.py
@@ -293,7 +293,7 @@ def set_exception(self, exception):
         self.__schedule_callbacks()
         self.__log_traceback = True
 
-    def __await__(self):
+    def _await(self):
         if not self.done():
             self._asyncio_future_blocking = True
             yield self  # This tells Task to wait for completion.
@@ -301,9 +301,43 @@ def __await__(self):
             raise RuntimeError("await wasn't used with future")
         return self.result()  # May raise too.
 
+    def __await__(self):
+        # Return an object that exposes fi_future in addition to being
+        # a generator, for compatability with the C version.
+        return _FutureIter(self)
+
     __iter__ = __await__  # make compatible with 'yield from'.
 
 
+class _FutureIter:
+    __slots__ = ('__future', '__gen')
+
+    def __init__(self, fut):
+        self.__future = fut
+        self.__gen = fut._await()
+
+    @property
+    def fi_future(self):
+        return self.__future
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        return self.__gen.__next__()
+
+    def send(self, v, /):
+        return self.__gen.send(v)
+
+    def throw(self, *args):
+        self.__future = None
+        return self.__gen.throw(*args)
+
+    def close(self):
+        self.__future = None
+        self.__gen.close()
+
+
 # Needed for testing purposes.
 _PyFuture = Future
 
diff --git a/Lib/test/test_asyncio/test_futures.py 
b/Lib/test/test_asyncio/test_futures.py
index 5ed3ec582224c6..b244301e98e7ee 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -712,6 +712,10 @@ def test_future_stop_iteration_args(self):
             self.fail('StopIteration was expected')
         self.assertEqual(result, (1, 2))
 
+    def test_future_iter_fut(self):
+        fut = self._new_future(loop=self.loop)
+        self.assertIs(iter(fut).fi_future, fut)
+
     def test_future_iter_throw(self):
         fut = self._new_future(loop=self.loop)
         fi = iter(fut)
diff --git 
a/Misc/NEWS.d/next/Library/2026-09-29-19-34-30.gh-issue-158192.6wPjiU.rst 
b/Misc/NEWS.d/next/Library/2026-09-29-19-34-30.gh-issue-158192.6wPjiU.rst
new file mode 100644
index 00000000000000..b0634084c6af8a
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2026-09-29-19-34-30.gh-issue-158192.6wPjiU.rst
@@ -0,0 +1 @@
+Expose the future being waited on by a ``FutureIter`` as ``fi_future``.
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index 5569f7a3670378..73404cb75b5d1f 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -1988,6 +1988,22 @@ static PyMethodDef FutureIter_methods[] = {
     {NULL, NULL}        /* Sentinel */
 };
 
+static PyObject *
+FutureIter_get_fi_future(PyObject *op, void *Py_UNUSED(closure))
+{
+    futureiterobject *it = (futureiterobject*)op;
+    PyObject *fut;
+    Py_BEGIN_CRITICAL_SECTION(op);
+    fut = it->future ? Py_NewRef(it->future) : Py_None;
+    Py_END_CRITICAL_SECTION();
+    return fut;
+}
+
+static PyGetSetDef FutureIter_getsetlist[] = {
+    {"fi_future", FutureIter_get_fi_future, NULL, NULL},
+    {NULL} /* Sentinel */
+};
+
 static PyType_Slot FutureIter_slots[] = {
     {Py_tp_dealloc, FutureIter_dealloc},
     {Py_tp_getattro, PyObject_GenericGetAttr},
@@ -1996,6 +2012,7 @@ static PyType_Slot FutureIter_slots[] = {
     {Py_tp_iter, PyObject_SelfIter},
     {Py_tp_iternext, FutureIter_iternext},
     {Py_tp_methods, FutureIter_methods},
+    {Py_tp_getset, FutureIter_getsetlist},
 
     // async methods
     {Py_am_send, FutureIter_am_send},

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to