bpo-29922: Improve error messages in 'async with' (GH-6352)
when __aenter__() or __aexit__() return non-awaitable object.
(cherry picked from commit a68f2f0578
)
Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
This commit is contained in:
parent
a5c8830637
commit
4fd6c27dc8
|
@ -1229,7 +1229,9 @@ class CoroutineTest(unittest.TestCase):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
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
|
# it's important that __aexit__ wasn't called
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
|
|
||||||
|
@ -1249,7 +1251,9 @@ class CoroutineTest(unittest.TestCase):
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
except TypeError as exc:
|
except TypeError as exc:
|
||||||
self.assertRegex(
|
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(exc.__context__ is not None)
|
||||||
self.assertTrue(isinstance(exc.__context__, ZeroDivisionError))
|
self.assertTrue(isinstance(exc.__context__, ZeroDivisionError))
|
||||||
else:
|
else:
|
||||||
|
@ -1273,8 +1277,9 @@ class CoroutineTest(unittest.TestCase):
|
||||||
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
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())
|
run_async(foo())
|
||||||
|
|
||||||
self.assertEqual(CNT, 1)
|
self.assertEqual(CNT, 1)
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Improved error messages in 'async with' when ``__aenter__()`` or
|
||||||
|
``__aexit__()`` return non-awaitable object.
|
|
@ -66,6 +66,7 @@ static PyObject * unicode_concatenate(PyObject *, PyObject *,
|
||||||
static PyObject * special_lookup(PyObject *, _Py_Identifier *);
|
static PyObject * special_lookup(PyObject *, _Py_Identifier *);
|
||||||
static int check_args_iterable(PyObject *func, PyObject *vararg);
|
static int check_args_iterable(PyObject *func, PyObject *vararg);
|
||||||
static void format_kwargs_mapping_error(PyObject *func, PyObject *kwargs);
|
static void format_kwargs_mapping_error(PyObject *func, PyObject *kwargs);
|
||||||
|
static void format_awaitable_error(PyTypeObject *, int);
|
||||||
|
|
||||||
#define NAME_ERROR_MSG \
|
#define NAME_ERROR_MSG \
|
||||||
"name '%.200s' is not defined"
|
"name '%.200s' is not defined"
|
||||||
|
@ -2040,6 +2041,11 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag)
|
||||||
PyObject *iterable = TOP();
|
PyObject *iterable = TOP();
|
||||||
PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
|
PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
|
||||||
|
|
||||||
|
if (iter == NULL) {
|
||||||
|
format_awaitable_error(Py_TYPE(iterable),
|
||||||
|
_Py_OPCODE(next_instr[-2]));
|
||||||
|
}
|
||||||
|
|
||||||
Py_DECREF(iterable);
|
Py_DECREF(iterable);
|
||||||
|
|
||||||
if (iter != NULL && PyCoro_CheckExact(iter)) {
|
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 *
|
static PyObject *
|
||||||
unicode_concatenate(PyObject *v, PyObject *w,
|
unicode_concatenate(PyObject *v, PyObject *w,
|
||||||
PyFrameObject *f, const _Py_CODEUNIT *next_instr)
|
PyFrameObject *f, const _Py_CODEUNIT *next_instr)
|
||||||
|
|
Loading…
Reference in New Issue