From a4b884f9009e9797e8bb7f8c9e797b4f033dd37e Mon Sep 17 00:00:00 2001
From: Yury Selivanov <yury@magic.io>
Date: Thu, 20 Oct 2016 15:54:20 -0400
Subject: Issue #28492: Fix how StopIteration is raised in _asyncio.Future

---
 Lib/test/test_asyncio/test_futures.py | 13 +++++++++++++
 Misc/NEWS                             |  2 ++
 Modules/_asynciomodule.c              | 21 +++++++++++++++++++--
 3 files changed, 34 insertions(+), 2 deletions(-)

diff --git a/Lib/test/test_asyncio/test_futures.py b/Lib/test/test_asyncio/test_futures.py
index d20eb68..6916b51 100644
--- a/Lib/test/test_asyncio/test_futures.py
+++ b/Lib/test/test_asyncio/test_futures.py
@@ -464,6 +464,19 @@ class FutureTests(test_utils.TestCase):
         futures._set_result_unless_cancelled(fut, 2)
         self.assertTrue(fut.cancelled())
 
+    def test_future_stop_iteration_args(self):
+        fut = asyncio.Future(loop=self.loop)
+        fut.set_result((1, 2))
+        fi = fut.__iter__()
+        result = None
+        try:
+            fi.send(None)
+        except StopIteration as ex:
+            result = ex.args[0]
+        else:
+            self.fail('StopIteration was expected')
+        self.assertEqual(result, (1, 2))
+
 
 class FutureDoneCallbackTests(test_utils.TestCase):
 
diff --git a/Misc/NEWS b/Misc/NEWS
index 2936e3a..3760bc7 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -28,6 +28,8 @@ Library
 - Issue #20766: Fix references leaked by pdb in the handling of SIGINT
   handlers.
 
+- Issue #28492: Fix how StopIteration exception is raised in _asyncio.Future.
+
 Build
 -----
 
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index d1d9c54..d9fe63d 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -787,9 +787,26 @@ FutureIter_iternext(futureiterobject *it)
 
     res = FutureObj_result(fut, NULL);
     if (res != NULL) {
-        // normal result
-        PyErr_SetObject(PyExc_StopIteration, res);
+        /* The result of the Future is not an exception.
+
+           We cunstruct an exception instance manually with
+           PyObject_CallFunctionObjArgs and pass it to PyErr_SetObject
+           (similarly to what genobject.c does).
+
+           This is to handle a situation when "res" is a tuple, in which
+           case PyErr_SetObject would set the value of StopIteration to
+           the first element of the tuple.
+
+           (See PyErr_SetObject/_PyErr_CreateException code for details.)
+        */
+        PyObject *e = PyObject_CallFunctionObjArgs(
+            PyExc_StopIteration, res, NULL);
         Py_DECREF(res);
+        if (e == NULL) {
+            return NULL;
+        }
+        PyErr_SetObject(PyExc_StopIteration, e);
+        Py_DECREF(e);
     }
 
     it->future = NULL;
-- 
cgit v0.12

