Skip to content

Commit 4fd6c27

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 a5c8830 commit 4fd6c27

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
@@ -1229,7 +1229,9 @@ async def foo():
12291229
pass
12301230

12311231
with self.assertRaisesRegex(
1232-
TypeError, "object int can't be used in 'await' expression"):
1232+
TypeError,
1233+
"'async with' received an object from __aenter__ "
1234+
"that does not implement __await__: int"):
12331235
# it's important that __aexit__ wasn't called
12341236
run_async(foo())
12351237

@@ -1249,7 +1251,9 @@ async def foo():
12491251
run_async(foo())
12501252
except TypeError as exc:
12511253
self.assertRegex(
1252-
exc.args[0], "object int can't be used in 'await' expression")
1254+
exc.args[0],
1255+
"'async with' received an object from __aexit__ "
1256+
"that does not implement __await__: int")
12531257
self.assertTrue(exc.__context__ is not None)
12541258
self.assertTrue(isinstance(exc.__context__, ZeroDivisionError))
12551259
else:
@@ -1273,8 +1277,9 @@ async def foo():
12731277

12741278

12751279
with self.assertRaisesRegex(
1276-
TypeError, "object int can't be used in 'await' expression"):
1277-
1280+
TypeError,
1281+
"'async with' received an object from __aexit__ "
1282+
"that does not implement __await__: int"):
12781283
run_async(foo())
12791284

12801285
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
@@ -66,6 +66,7 @@ static PyObject * unicode_concatenate(PyObject *, PyObject *,
6666
static PyObject * special_lookup(PyObject *, _Py_Identifier *);
6767
static int check_args_iterable(PyObject *func, PyObject *vararg);
6868
static void format_kwargs_mapping_error(PyObject *func, PyObject *kwargs);
69+
static void format_awaitable_error(PyTypeObject *, int);
6970

7071
#define NAME_ERROR_MSG \
7172
"name '%.200s' is not defined"
@@ -2040,6 +2041,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
20402041
PyObject *iterable = TOP();
20412042
PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
20422043

2044+
if (iter == NULL) {
2045+
format_awaitable_error(Py_TYPE(iterable),
2046+
_Py_OPCODE(next_instr[-2]));
2047+
}
2048+
20432049
Py_DECREF(iterable);
20442050

20452051
if (iter != NULL && PyCoro_CheckExact(iter)) {
@@ -5403,6 +5409,25 @@ format_exc_unbound(PyCodeObject *co, int oparg)
54035409
}
54045410
}
54055411

5412+
static void
5413+
format_awaitable_error(PyTypeObject *type, int prevopcode)
5414+
{
5415+
if (type->tp_as_async == NULL || type->tp_as_async->am_await == NULL) {
5416+
if (prevopcode == BEFORE_ASYNC_WITH) {
5417+
PyErr_Format(PyExc_TypeError,
5418+
"'async with' received an object from __aenter__ "
5419+
"that does not implement __await__: %.100s",
5420+
type->tp_name);
5421+
}
5422+
else if (prevopcode == WITH_CLEANUP_START) {
5423+
PyErr_Format(PyExc_TypeError,
5424+
"'async with' received an object from __aexit__ "
5425+
"that does not implement __await__: %.100s",
5426+
type->tp_name);
5427+
}
5428+
}
5429+
}
5430+
54065431
static PyObject *
54075432
unicode_concatenate(PyObject *v, PyObject *w,
54085433
PyFrameObject *f, const _Py_CODEUNIT *next_instr)

0 commit comments

Comments
 (0)