summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGéry Ogam <gery.ogam@gmail.com>2020-01-14 11:58:29 (GMT)
committerNick Coghlan <ncoghlan@gmail.com>2020-01-14 11:58:29 (GMT)
commit1d1b97ae643dd8b22d87785ed7bd2599c6c8dc8d (patch)
tree1cecc3868d762cd3e1c2414520d954dfe05aea17
parent9af0e47b1705457bb6b327c197f2ec5737a1d8f6 (diff)
downloadcpython-1d1b97ae643dd8b22d87785ed7bd2599c6c8dc8d.zip
cpython-1d1b97ae643dd8b22d87785ed7bd2599c6c8dc8d.tar.gz
cpython-1d1b97ae643dd8b22d87785ed7bd2599c6c8dc8d.tar.bz2
bpo-39048: Look up __aenter__ before __aexit__ in async with (GH-17609)
* Reorder the __aenter__ and __aexit__ checks for async with * Add assertions for async with body being skipped * Swap __aexit__ and __aenter__ loading in the documentation
-rw-r--r--Doc/reference/compound_stmts.rst2
-rw-r--r--Lib/test/test_coroutines.py20
-rw-r--r--Misc/ACKS1
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2020-01-13-14-45-22.bpo-39048.iPsj81.rst4
-rw-r--r--Python/ceval.c19
5 files changed, 27 insertions, 19 deletions
diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst
index 564d6cc..e2f44a5 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 208b5c2..8d1e069 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 d3e683d..3e45d5d 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 0000000..1179ef4
--- /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 096645a..5e58658 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;