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
This commit is contained in:
parent
9af0e47b17
commit
1d1b97ae64
|
@ -844,8 +844,8 @@ The following code::
|
||||||
is semantically equivalent to::
|
is semantically equivalent to::
|
||||||
|
|
||||||
manager = (EXPRESSION)
|
manager = (EXPRESSION)
|
||||||
aexit = type(manager).__aexit__
|
|
||||||
aenter = type(manager).__aenter__
|
aenter = type(manager).__aenter__
|
||||||
|
aexit = type(manager).__aexit__
|
||||||
value = await aenter(manager)
|
value = await aenter(manager)
|
||||||
hit_except = False
|
hit_except = False
|
||||||
|
|
||||||
|
|
|
@ -1203,39 +1203,41 @@ class CoroutineTest(unittest.TestCase):
|
||||||
def __aenter__(self):
|
def __aenter__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
body_executed = False
|
||||||
async def foo():
|
async def foo():
|
||||||
async with CM():
|
async with CM():
|
||||||
pass
|
body_executed = True
|
||||||
|
|
||||||
with self.assertRaisesRegex(AttributeError, '__aexit__'):
|
with self.assertRaisesRegex(AttributeError, '__aexit__'):
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
|
self.assertFalse(body_executed)
|
||||||
|
|
||||||
def test_with_3(self):
|
def test_with_3(self):
|
||||||
class CM:
|
class CM:
|
||||||
def __aexit__(self):
|
def __aexit__(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
body_executed = False
|
||||||
async def foo():
|
async def foo():
|
||||||
async with CM():
|
async with CM():
|
||||||
pass
|
body_executed = True
|
||||||
|
|
||||||
with self.assertRaisesRegex(AttributeError, '__aenter__'):
|
with self.assertRaisesRegex(AttributeError, '__aenter__'):
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
|
self.assertFalse(body_executed)
|
||||||
|
|
||||||
def test_with_4(self):
|
def test_with_4(self):
|
||||||
class CM:
|
class CM:
|
||||||
def __enter__(self):
|
pass
|
||||||
pass
|
|
||||||
|
|
||||||
def __exit__(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
body_executed = False
|
||||||
async def foo():
|
async def foo():
|
||||||
async with CM():
|
async with CM():
|
||||||
pass
|
body_executed = True
|
||||||
|
|
||||||
with self.assertRaisesRegex(AttributeError, '__aexit__'):
|
with self.assertRaisesRegex(AttributeError, '__aenter__'):
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
|
self.assertFalse(body_executed)
|
||||||
|
|
||||||
def test_with_5(self):
|
def test_with_5(self):
|
||||||
# While this test doesn't make a lot of sense,
|
# While this test doesn't make a lot of sense,
|
||||||
|
|
|
@ -1219,6 +1219,7 @@ Elena Oat
|
||||||
Jon Oberheide
|
Jon Oberheide
|
||||||
Milan Oberkirch
|
Milan Oberkirch
|
||||||
Pascal Oberndoerfer
|
Pascal Oberndoerfer
|
||||||
|
Géry Ogam
|
||||||
Jeffrey Ollie
|
Jeffrey Ollie
|
||||||
Adam Olsen
|
Adam Olsen
|
||||||
Bryan Olson
|
Bryan Olson
|
||||||
|
|
|
@ -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.
|
|
@ -3230,20 +3230,21 @@ main_loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
case TARGET(BEFORE_ASYNC_WITH): {
|
case TARGET(BEFORE_ASYNC_WITH): {
|
||||||
_Py_IDENTIFIER(__aexit__);
|
|
||||||
_Py_IDENTIFIER(__aenter__);
|
_Py_IDENTIFIER(__aenter__);
|
||||||
|
_Py_IDENTIFIER(__aexit__);
|
||||||
PyObject *mgr = TOP();
|
PyObject *mgr = TOP();
|
||||||
PyObject *exit = special_lookup(tstate, mgr, &PyId___aexit__),
|
PyObject *enter = special_lookup(tstate, mgr, &PyId___aenter__);
|
||||||
*enter;
|
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
if (exit == NULL)
|
if (enter == NULL) {
|
||||||
goto error;
|
goto error;
|
||||||
|
}
|
||||||
|
PyObject *exit = special_lookup(tstate, mgr, &PyId___aexit__);
|
||||||
|
if (exit == NULL) {
|
||||||
|
Py_DECREF(enter);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
SET_TOP(exit);
|
SET_TOP(exit);
|
||||||
enter = special_lookup(tstate, mgr, &PyId___aenter__);
|
|
||||||
Py_DECREF(mgr);
|
Py_DECREF(mgr);
|
||||||
if (enter == NULL)
|
|
||||||
goto error;
|
|
||||||
res = _PyObject_CallNoArg(enter);
|
res = _PyObject_CallNoArg(enter);
|
||||||
Py_DECREF(enter);
|
Py_DECREF(enter);
|
||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
|
@ -3264,8 +3265,8 @@ main_loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
case TARGET(SETUP_WITH): {
|
case TARGET(SETUP_WITH): {
|
||||||
_Py_IDENTIFIER(__exit__);
|
|
||||||
_Py_IDENTIFIER(__enter__);
|
_Py_IDENTIFIER(__enter__);
|
||||||
|
_Py_IDENTIFIER(__exit__);
|
||||||
PyObject *mgr = TOP();
|
PyObject *mgr = TOP();
|
||||||
PyObject *enter = special_lookup(tstate, mgr, &PyId___enter__);
|
PyObject *enter = special_lookup(tstate, mgr, &PyId___enter__);
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
|
|
Loading…
Reference in New Issue