From f0dbc4b692cbf87636b23b40faadf0c49828f553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Sat, 14 Dec 2019 20:20:37 +0100 Subject: [PATCH 1/5] Reorder the __aenter__ and __aexit__ checks for async with --- Lib/test/test_coroutines.py | 8 ++------ Python/ceval.c | 15 ++++++++------- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 208b5c2ccf5cd2..33812cc014a484 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -1224,17 +1224,13 @@ async def foo(): def test_with_4(self): class CM: - def __enter__(self): - pass - - def __exit__(self): - pass + pass async def foo(): async with CM(): pass - with self.assertRaisesRegex(AttributeError, '__aexit__'): + with self.assertRaisesRegex(AttributeError, '__aenter__'): run_async(foo()) def test_with_5(self): diff --git a/Python/ceval.c b/Python/ceval.c index 3bbd0ca9667b0d..61ecb40b57de02 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3156,18 +3156,19 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) case TARGET(BEFORE_ASYNC_WITH): { _Py_IDENTIFIER(__aexit__); _Py_IDENTIFIER(__aenter__); - 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) From 940e93247b1e84f992fb27559d641c67b147c3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Mon, 13 Jan 2020 14:24:35 +0100 Subject: [PATCH 2/5] Add assertions for async with body --- Lib/test/test_coroutines.py | 12 +++++++++--- Python/ceval.c | 4 ++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Lib/test/test_coroutines.py b/Lib/test/test_coroutines.py index 33812cc014a484..8d1e0692a24221 100644 --- a/Lib/test/test_coroutines.py +++ b/Lib/test/test_coroutines.py @@ -1203,35 +1203,41 @@ class CM: 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: 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_5(self): # While this test doesn't make a lot of sense, diff --git a/Python/ceval.c b/Python/ceval.c index 61ecb40b57de02..6cfac2d787f171 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -3154,8 +3154,8 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } case TARGET(BEFORE_ASYNC_WITH): { - _Py_IDENTIFIER(__aexit__); _Py_IDENTIFIER(__aenter__); + _Py_IDENTIFIER(__aexit__); PyObject *mgr = TOP(); PyObject *enter = special_lookup(tstate, mgr, &PyId___aenter__); PyObject *res; @@ -3189,8 +3189,8 @@ _PyEval_EvalFrameDefault(PyFrameObject *f, int throwflag) } 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; From fe16404b42a3be2b6329285da9326ac87e901904 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Mon, 13 Jan 2020 14:26:51 +0100 Subject: [PATCH 3/5] Swap __aexit__ and __aenter__ loading in the documentation --- Doc/reference/compound_stmts.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index 564d6cc42136da..e2f44a55b180b1 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 From b2d3d83400761ecf0682ce672da957221b72273a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Mon, 13 Jan 2020 15:01:56 +0100 Subject: [PATCH 4/5] Add ACKS and NEWS entries --- Misc/ACKS | 1 + .../Core and Builtins/2020-01-13-14-45-22.bpo-39048.iPsj81.rst | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2020-01-13-14-45-22.bpo-39048.iPsj81.rst diff --git a/Misc/ACKS b/Misc/ACKS index d3e683d4a085fd..3e45d5d0f7f29d 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 00000000000000..5549ea63201002 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2020-01-13-14-45-22.bpo-39048.iPsj81.rst @@ -0,0 +1,2 @@ +Change the lookup order of the :meth:`__aenter__` and :meth:`__aexit__` methods +for the ``async with`` statement. Patch by Géry Ogam. From df005dbbc3e1934aadc7dbbebf7497bce8534213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A9ry=20Ogam?= Date: Tue, 14 Jan 2020 12:15:11 +0100 Subject: [PATCH 5/5] Improve the NEWS entry --- .../2020-01-13-14-45-22.bpo-39048.iPsj81.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 index 5549ea63201002..1179ef49651bd8 100644 --- 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 @@ -1,2 +1,4 @@ -Change the lookup order of the :meth:`__aenter__` and :meth:`__aexit__` methods -for the ``async with`` statement. Patch by Géry Ogam. +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.