Issue 24017: fix for "async with" refcounting
* adds missing INCREF in WITH_CLEANUP_START * adds missing DECREF in WITH_CLEANUP_FINISH * adds several new tests Yury created while investigating this
This commit is contained in:
parent
84d3e764d7
commit
baaadbf70d
@ -497,17 +497,133 @@ class CoroutineTest(unittest.TestCase):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def __aexit__(self, *e):
|
def __aexit__(self, *e):
|
||||||
return 456
|
return 444
|
||||||
|
|
||||||
async def foo():
|
async def foo():
|
||||||
async with CM():
|
async with CM():
|
||||||
pass
|
1/0
|
||||||
|
|
||||||
|
try:
|
||||||
|
run_async(foo())
|
||||||
|
except TypeError as exc:
|
||||||
|
self.assertRegex(
|
||||||
|
exc.args[0], "object int can't be used in 'await' expression")
|
||||||
|
self.assertTrue(exc.__context__ is not None)
|
||||||
|
self.assertTrue(isinstance(exc.__context__, ZeroDivisionError))
|
||||||
|
else:
|
||||||
|
self.fail('invalid asynchronous context manager did not fail')
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_8(self):
|
||||||
|
CNT = 0
|
||||||
|
|
||||||
|
class CM:
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __aexit__(self, *e):
|
||||||
|
return 456
|
||||||
|
|
||||||
|
async def foo():
|
||||||
|
nonlocal CNT
|
||||||
|
async with CM():
|
||||||
|
CNT += 1
|
||||||
|
|
||||||
|
|
||||||
with self.assertRaisesRegex(
|
with self.assertRaisesRegex(
|
||||||
TypeError, "object int can't be used in 'await' expression"):
|
TypeError, "object int can't be used in 'await' expression"):
|
||||||
|
|
||||||
run_async(foo())
|
run_async(foo())
|
||||||
|
|
||||||
|
self.assertEqual(CNT, 1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_with_9(self):
|
||||||
|
CNT = 0
|
||||||
|
|
||||||
|
class CM:
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, *e):
|
||||||
|
1/0
|
||||||
|
|
||||||
|
async def foo():
|
||||||
|
nonlocal CNT
|
||||||
|
async with CM():
|
||||||
|
CNT += 1
|
||||||
|
|
||||||
|
with self.assertRaises(ZeroDivisionError):
|
||||||
|
run_async(foo())
|
||||||
|
|
||||||
|
self.assertEqual(CNT, 1)
|
||||||
|
|
||||||
|
def test_with_10(self):
|
||||||
|
CNT = 0
|
||||||
|
|
||||||
|
class CM:
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, *e):
|
||||||
|
1/0
|
||||||
|
|
||||||
|
async def foo():
|
||||||
|
nonlocal CNT
|
||||||
|
async with CM():
|
||||||
|
async with CM():
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
try:
|
||||||
|
run_async(foo())
|
||||||
|
except ZeroDivisionError as exc:
|
||||||
|
self.assertTrue(exc.__context__ is not None)
|
||||||
|
self.assertTrue(isinstance(exc.__context__, ZeroDivisionError))
|
||||||
|
self.assertTrue(isinstance(exc.__context__.__context__,
|
||||||
|
RuntimeError))
|
||||||
|
else:
|
||||||
|
self.fail('exception from __aexit__ did not propagate')
|
||||||
|
|
||||||
|
def test_with_11(self):
|
||||||
|
CNT = 0
|
||||||
|
|
||||||
|
class CM:
|
||||||
|
async def __aenter__(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
async def __aexit__(self, *e):
|
||||||
|
1/0
|
||||||
|
|
||||||
|
async def foo():
|
||||||
|
nonlocal CNT
|
||||||
|
async with CM():
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
try:
|
||||||
|
run_async(foo())
|
||||||
|
except NotImplementedError as exc:
|
||||||
|
self.assertTrue(exc.__context__ is None)
|
||||||
|
else:
|
||||||
|
self.fail('exception from __aenter__ did not propagate')
|
||||||
|
|
||||||
|
def test_with_12(self):
|
||||||
|
CNT = 0
|
||||||
|
|
||||||
|
class CM:
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, *e):
|
||||||
|
return True
|
||||||
|
|
||||||
|
async def foo():
|
||||||
|
nonlocal CNT
|
||||||
|
async with CM() as cm:
|
||||||
|
self.assertIs(cm.__class__, CM)
|
||||||
|
raise RuntimeError
|
||||||
|
|
||||||
|
run_async(foo())
|
||||||
|
|
||||||
def test_for_1(self):
|
def test_for_1(self):
|
||||||
aiter_calls = 0
|
aiter_calls = 0
|
||||||
|
|
||||||
|
@ -3156,6 +3156,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||||||
if (res == NULL)
|
if (res == NULL)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
Py_INCREF(exc); /* Duplicating the exception on the stack */
|
||||||
PUSH(exc);
|
PUSH(exc);
|
||||||
PUSH(res);
|
PUSH(res);
|
||||||
PREDICT(WITH_CLEANUP_FINISH);
|
PREDICT(WITH_CLEANUP_FINISH);
|
||||||
@ -3174,6 +3175,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||||||
err = 0;
|
err = 0;
|
||||||
|
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
|
Py_DECREF(exc);
|
||||||
|
|
||||||
if (err < 0)
|
if (err < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user