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::
|
||||
|
||||
manager = (EXPRESSION)
|
||||
aexit = type(manager).__aexit__
|
||||
aenter = type(manager).__aenter__
|
||||
aexit = type(manager).__aexit__
|
||||
value = await aenter(manager)
|
||||
hit_except = False
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -1219,6 +1219,7 @@ Elena Oat
|
|||
Jon Oberheide
|
||||
Milan Oberkirch
|
||||
Pascal Oberndoerfer
|
||||
Géry Ogam
|
||||
Jeffrey Ollie
|
||||
Adam Olsen
|
||||
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): {
|
||||
_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;
|
||||
|
|
Loading…
Reference in New Issue