]> granicus.if.org Git - python/commitdiff
Issue #28492: Fix how StopIteration is raised in _asyncio.Future
authorYury Selivanov <yury@magic.io>
Thu, 20 Oct 2016 19:54:20 +0000 (15:54 -0400)
committerYury Selivanov <yury@magic.io>
Thu, 20 Oct 2016 19:54:20 +0000 (15:54 -0400)
Lib/test/test_asyncio/test_futures.py
Misc/NEWS
Modules/_asynciomodule.c

index d20eb687f9a2200f68e5cd49d8daaadaea885ca1..6916b513e805d7d787634e470526da1c2cc9a185 100644 (file)
@@ -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):
 
index 2936e3a329f51e2bbb1a8db525d14c3c8c9a6bed..3760bc78da93549084701d598f73ca55dc2d7ada 100644 (file)
--- 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
 -----
 
index d1d9c5425ef896610d2ce44a37c25825bf5f74bf..d9fe63d320a5716cce722cd6217233b62c782e30 100644 (file)
@@ -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;