Skip to content

Commit

Permalink
bpo-29922: Improve error messages in 'async with' (GH-6352)
Browse files Browse the repository at this point in the history
when __aenter__() or __aexit__() return non-awaitable object.
(cherry picked from commit a68f2f0)

Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
  • Loading branch information
miss-islington and serhiy-storchaka committed Apr 4, 2018
1 parent b8fc2d6 commit fcd4e03
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 4 deletions.
13 changes: 9 additions & 4 deletions Lib/test/test_coroutines.py
Original file line number Diff line number Diff line change
Expand Up @@ -1255,7 +1255,9 @@ async def foo():
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())

Expand All @@ -1275,7 +1277,9 @@ async def foo():
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:
Expand All @@ -1299,8 +1303,9 @@ async def foo():


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)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Improved error messages in 'async with' when ``__aenter__()`` or
``__aexit__()`` return non-awaitable object.
25 changes: 25 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,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"
Expand Down Expand Up @@ -1739,6 +1740,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)) {
Expand Down Expand Up @@ -4948,6 +4954,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)
Expand Down

0 comments on commit fcd4e03

Please sign in to comment.