[Python-checkins] bpo-29922: Improve error messages in 'async with' (GH-6352)

Miss Islington (bot) webhook-mailer at python.org
Wed Apr 4 10:09:17 EDT 2018


https://github.com/python/cpython/commit/4fd6c27dc8ba7ca97aa70e1ab98729f2207bbe19
commit: 4fd6c27dc8ba7ca97aa70e1ab98729f2207bbe19
branch: 3.6
author: Miss Islington (bot) <31488909+miss-islington at users.noreply.github.com>
committer: GitHub <noreply at github.com>
date: 2018-04-04T07:09:14-07:00
summary:

bpo-29922: Improve error messages in 'async with' (GH-6352)


when __aenter__() or __aexit__() return non-awaitable object.
(cherry picked from commit a68f2f0578bbf812fa2264d0e0bb388340d6e230)

Co-authored-by: Serhiy Storchaka <storchaka at gmail.com>

files:
A Misc/NEWS.d/next/Core and Builtins/2018-04-03-00-30-25.bpo-29922.CdLuMl.rst
M Lib/test/test_coroutines.py
M Python/ceval.c

diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py
index 41126b675b5c..769879f082c5 100644
--- a/Lib/test/test_coroutines.py
+++ b/Lib/test/test_coroutines.py
@@ -1229,7 +1229,9 @@ def __aexit__(self, *e):
                 pass
 
         with self.assertRaisesRegex(
-            TypeError, "object int can't be used in 'await' expression"):
+                TypeError,
+                "'async with' received an object from __aenter__ "
+                "that does not implement __await__: int"):
             # it's important that __aexit__ wasn't called
             run_async(foo())
 
@@ -1249,7 +1251,9 @@ def __aexit__(self, *e):
             run_async(foo())
         except TypeError as exc:
             self.assertRegex(
-                exc.args[0], "object int can't be used in 'await' expression")
+                exc.args[0],
+                "'async with' received an object from __aexit__ "
+                "that does not implement __await__: int")
             self.assertTrue(exc.__context__ is not None)
             self.assertTrue(isinstance(exc.__context__, ZeroDivisionError))
         else:
@@ -1273,8 +1277,9 @@ def __aexit__(self, *e):
 
 
         with self.assertRaisesRegex(
-            TypeError, "object int can't be used in 'await' expression"):
-
+                TypeError,
+                "'async with' received an object from __aexit__ "
+                "that does not implement __await__: int"):
             run_async(foo())
 
         self.assertEqual(CNT, 1)
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-04-03-00-30-25.bpo-29922.CdLuMl.rst b/Misc/NEWS.d/next/Core and Builtins/2018-04-03-00-30-25.bpo-29922.CdLuMl.rst
new file mode 100644
index 000000000000..d8c144e59d6c
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-04-03-00-30-25.bpo-29922.CdLuMl.rst	
@@ -0,0 +1,2 @@
+Improved error messages in 'async with' when ``__aenter__()`` or
+``__aexit__()`` return non-awaitable object.
diff --git a/Python/ceval.c b/Python/ceval.c
index 9ad582b15c57..6252e89c9106 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -66,6 +66,7 @@ static PyObject * unicode_concatenate(PyObject *, PyObject *,
 static PyObject * special_lookup(PyObject *, _Py_Identifier *);
 static int check_args_iterable(PyObject *func, PyObject *vararg);
 static void format_kwargs_mapping_error(PyObject *func, PyObject *kwargs);
+static void format_awaitable_error(PyTypeObject *, int);
 
 #define NAME_ERROR_MSG \
     "name '%.200s' is not defined"
@@ -2040,6 +2041,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
             PyObject *iterable = TOP();
             PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
 
+            if (iter == NULL) {
+                format_awaitable_error(Py_TYPE(iterable),
+                                       _Py_OPCODE(next_instr[-2]));
+            }
+
             Py_DECREF(iterable);
 
             if (iter != NULL && PyCoro_CheckExact(iter)) {
@@ -5403,6 +5409,25 @@ format_exc_unbound(PyCodeObject *co, int oparg)
     }
 }
 
+static void
+format_awaitable_error(PyTypeObject *type, int prevopcode)
+{
+    if (type->tp_as_async == NULL || type->tp_as_async->am_await == NULL) {
+        if (prevopcode == BEFORE_ASYNC_WITH) {
+            PyErr_Format(PyExc_TypeError,
+                         "'async with' received an object from __aenter__ "
+                         "that does not implement __await__: %.100s",
+                         type->tp_name);
+        }
+        else if (prevopcode == WITH_CLEANUP_START) {
+            PyErr_Format(PyExc_TypeError,
+                         "'async with' received an object from __aexit__ "
+                         "that does not implement __await__: %.100s",
+                         type->tp_name);
+        }
+    }
+}
+
 static PyObject *
 unicode_concatenate(PyObject *v, PyObject *w,
                     PyFrameObject *f, const _Py_CODEUNIT *next_instr)



More information about the Python-checkins mailing list