Skip to content

Commit fcd4e03

Browse files
bpo-29922: Improve error messages in 'async with' (GH-6352)
when __aenter__() or __aexit__() return non-awaitable object. (cherry picked from commit a68f2f0) Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent b8fc2d6 commit fcd4e03

File tree

3 files changed

+36
-4
lines changed

3 files changed

+36
-4
lines changed

Lib/test/test_coroutines.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1255,7 +1255,9 @@ async def foo():
12551255
pass
12561256

12571257
with self.assertRaisesRegex(
1258-
TypeError, "object int can't be used in 'await' expression"):
1258+
TypeError,
1259+
"'async with' received an object from __aenter__ "
1260+
"that does not implement __await__: int"):
12591261
# it's important that __aexit__ wasn't called
12601262
run_async(foo())
12611263

@@ -1275,7 +1277,9 @@ async def foo():
12751277
run_async(foo())
12761278
except TypeError as exc:
12771279
self.assertRegex(
1278-
exc.args[0], "object int can't be used in 'await' expression")
1280+
exc.args[0],
1281+
"'async with' received an object from __aexit__ "
1282+
"that does not implement __await__: int")
12791283
self.assertTrue(exc.__context__ is not None)
12801284
self.assertTrue(isinstance(exc.__context__, ZeroDivisionError))
12811285
else:
@@ -1299,8 +1303,9 @@ async def foo():
12991303

13001304

13011305
with self.assertRaisesRegex(
1302-
TypeError, "object int can't be used in 'await' expression"):
1303-
1306+
TypeError,
1307+
"'async with' received an object from __aexit__ "
1308+
"that does not implement __await__: int"):
13041309
run_async(foo())
13051310

13061311
self.assertEqual(CNT, 1)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Improved error messages in 'async with' when ``__aenter__()`` or
2+
``__aexit__()`` return non-awaitable object.

Python/ceval.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ static PyObject * unicode_concatenate(PyObject *, PyObject *,
6969
static PyObject * special_lookup(PyObject *, _Py_Identifier *);
7070
static int check_args_iterable(PyObject *func, PyObject *vararg);
7171
static void format_kwargs_mapping_error(PyObject *func, PyObject *kwargs);
72+
static void format_awaitable_error(PyTypeObject *, int);
7273

7374
#define NAME_ERROR_MSG \
7475
"name '%.200s' is not defined"
@@ -1739,6 +1740,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
17391740
PyObject *iterable = TOP();
17401741
PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
17411742

1743+
if (iter == NULL) {
1744+
format_awaitable_error(Py_TYPE(iterable),
1745+
_Py_OPCODE(next_instr[-2]));
1746+
}
1747+
17421748
Py_DECREF(iterable);
17431749

17441750
if (iter != NULL && PyCoro_CheckExact(iter)) {
@@ -4948,6 +4954,25 @@ format_exc_unbound(PyCodeObject *co, int oparg)
49484954
}
49494955
}
49504956

4957+
static void
4958+
format_awaitable_error(PyTypeObject *type, int prevopcode)
4959+
{
4960+
if (type->tp_as_async == NULL || type->tp_as_async->am_await == NULL) {
4961+
if (prevopcode == BEFORE_ASYNC_WITH) {
4962+
PyErr_Format(PyExc_TypeError,
4963+
"'async with' received an object from __aenter__ "
4964+
"that does not implement __await__: %.100s",
4965+
type->tp_name);
4966+
}
4967+
else if (prevopcode == WITH_CLEANUP_START) {
4968+
PyErr_Format(PyExc_TypeError,
4969+
"'async with' received an object from __aexit__ "
4970+
"that does not implement __await__: %.100s",
4971+
type->tp_name);
4972+
}
4973+
}
4974+
}
4975+
49514976
static PyObject *
49524977
unicode_concatenate(PyObject *v, PyObject *w,
49534978
PyFrameObject *f, const _Py_CODEUNIT *next_instr)

0 commit comments

Comments
 (0)