diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 564d6cc421..e2f44a55b1 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -844,8 +844,8 @@ The following code:: is semantically equivalent to:: manager = (EXPRESSION) - aexit = type(manager).__aexit__ aenter = type(manager).__aenter__ + aexit = type(manager).__aexit__ value = await aenter(manager) hit_except = False diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 208b5c2ccf..8d1e0692a2 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -1203,39 +1203,41 @@ class CoroutineTest(unittest.TestCase): def __aenter__(self): pass + body_executed = False async def foo(): async with CM(): - pass + body_executed = True with self.assertRaisesRegex(AttributeError, '__aexit__'): run_async(foo()) + self.assertFalse(body_executed) def test_with_3(self): class CM: def __aexit__(self): pass + body_executed = False async def foo(): async with CM(): - pass + body_executed = True with self.assertRaisesRegex(AttributeError, '__aenter__'): run_async(foo()) + self.assertFalse(body_executed) def test_with_4(self): class CM: - def __enter__(self): - pass - - def __exit__(self): - pass + pass + body_executed = False async def foo(): async with CM(): - pass + body_executed = True - with self.assertRaisesRegex(AttributeError, '__aexit__'): + with self.assertRaisesRegex(AttributeError, '__aenter__'): run_async(foo()) + self.assertFalse(body_executed) def test_with_5(self): # While this test doesn't make a lot of sense, diff --git a/Misc/ACKS b/Misc/ACKS index d3e683d4a0..3e45d5d0f7 100644 --- a/Misc/ACKS +++ b/Misc/ACKS @@ -1219,6 +1219,7 @@ Elena Oat Jon Oberheide Milan Oberkirch Pascal Oberndoerfer +Géry Ogam Jeffrey Ollie Adam Olsen Bryan Olson diff --git a/Misc/NEWS.d/next/Core and Builtins/2020-01-13-14-45-22.bpo-39048.iPsj81.rst b/Misc/NEWS.d/next/Core and Builtins/2020-01-13-14-45-22.bpo-39048.iPsj81.rst new file mode 100644 index 0000000000..1179ef4965 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-01-13-14-45-22.bpo-39048.iPsj81.rst @@ -0,0 +1,4 @@ +Improve the displayed error message when incorrect types are passed to ``async +with`` statements by looking up the :meth:`__aenter__` special method before +the :meth:`__aexit__` special method when entering an asynchronous context +manager. Patch by Géry Ogam. diff --git a/Python/ceval.c b/Python/ceval.c index 096645aeeb..5e586589e9 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3230,20 +3230,21 @@ main_loop: } case TARGET(BEFORE_ASYNC_WITH): { - _Py_IDENTIFIER(__aexit__); _Py_IDENTIFIER(__aenter__); - + _Py_IDENTIFIER(__aexit__); PyObject *mgr = TOP(); - PyObject *exit = special_lookup(tstate, mgr, &PyId___aexit__), - *enter; + PyObject *enter = special_lookup(tstate, mgr, &PyId___aenter__); PyObject *res; - if (exit == NULL) + if (enter == NULL) { goto error; + } + PyObject *exit = special_lookup(tstate, mgr, &PyId___aexit__); + if (exit == NULL) { + Py_DECREF(enter); + goto error; + } SET_TOP(exit); - enter = special_lookup(tstate, mgr, &PyId___aenter__); Py_DECREF(mgr); - if (enter == NULL) - goto error; res = _PyObject_CallNoArg(enter); Py_DECREF(enter); if (res == NULL) @@ -3264,8 +3265,8 @@ main_loop: } case TARGET(SETUP_WITH): { - _Py_IDENTIFIER(__exit__); _Py_IDENTIFIER(__enter__); + _Py_IDENTIFIER(__exit__); PyObject *mgr = TOP(); PyObject *enter = special_lookup(tstate, mgr, &PyId___enter__); PyObject *res;