Skip to content

Commit a68f2f0

Browse files
bpo-29922: Improve error messages in 'async with' (GH-6352)
when __aenter__() or __aexit__() return non-awaitable object.
1 parent 55966f3 commit a68f2f0

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"
@@ -1736,6 +1737,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
17361737
PyObject *iterable = TOP();
17371738
PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
17381739

1740+
if (iter == NULL) {
1741+
format_awaitable_error(Py_TYPE(iterable),
1742+
_Py_OPCODE(next_instr[-2]));
1743+
}
1744+
17391745
Py_DECREF(iterable);
17401746

17411747
if (iter != NULL && PyCoro_CheckExact(iter)) {
@@ -4985,6 +4991,25 @@ format_exc_unbound(PyCodeObject *co, int oparg)
49854991
}
49864992
}
49874993

4994+
static void
4995+
format_awaitable_error(PyTypeObject *type, int prevopcode)
4996+
{
4997+
if (type->tp_as_async == NULL || type->tp_as_async->am_await == NULL) {
4998+
if (prevopcode == BEFORE_ASYNC_WITH) {
4999+
PyErr_Format(PyExc_TypeError,
5000+
"'async with' received an object from __aenter__ "
5001+
"that does not implement __await__: %.100s",
5002+
type->tp_name);
5003+
}
5004+
else if (prevopcode == WITH_CLEANUP_START) {
5005+
PyErr_Format(PyExc_TypeError,
5006+
"'async with' received an object from __aexit__ "
5007+
"that does not implement __await__: %.100s",
5008+
type->tp_name);
5009+
}
5010+
}
5011+
}
5012+
49885013
static PyObject *
49895014
unicode_concatenate(PyObject *v, PyObject *w,
49905015
PyFrameObject *f, const _Py_CODEUNIT *next_instr)

0 commit comments

Comments
 (0)