summaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2020-05-22 21:35:22 (GMT)
committerGitHub <noreply@github.com>2020-05-22 21:35:22 (GMT)
commit7f77ac463cff219e0c8afef2611cad5080cc9df1 (patch)
tree8d28a7f990479fcf34d2493553a8583b515d7e39 /Lib
parenta08b7c3bb0ef9da32400d23b13f78245cd7a9541 (diff)
downloadcpython-7f77ac463cff219e0c8afef2611cad5080cc9df1.zip
cpython-7f77ac463cff219e0c8afef2611cad5080cc9df1.tar.gz
cpython-7f77ac463cff219e0c8afef2611cad5080cc9df1.tar.bz2
bpo-40696: Fix a hang that can arise after gen.throw() (GH-20287)
This updates _PyErr_ChainStackItem() to use _PyErr_SetObject() instead of _PyErr_ChainExceptions(). This prevents a hang in certain circumstances because _PyErr_SetObject() performs checks to prevent cycles in the exception context chain while _PyErr_ChainExceptions() doesn't. (cherry picked from commit 7c30d12bd5359b0f66c4fbc98aa055398bcc8a7e) Co-authored-by: Chris Jerdonek <chris.jerdonek@gmail.com>
Diffstat (limited to 'Lib')
-rw-r--r--Lib/test/test_asyncio/test_tasks.py39
-rw-r--r--Lib/test/test_generators.py26
2 files changed, 62 insertions, 3 deletions
diff --git a/Lib/test/test_asyncio/test_tasks.py b/Lib/test/test_asyncio/test_tasks.py
index 63968e2..3734013 100644
--- a/Lib/test/test_asyncio/test_tasks.py
+++ b/Lib/test/test_asyncio/test_tasks.py
@@ -536,9 +536,42 @@ class BaseTaskTests:
self.assertEqual((type(chained), chained.args),
(KeyError, (3,)))
- task = self.new_task(loop, run())
- loop.run_until_complete(task)
- loop.close()
+ try:
+ task = self.new_task(loop, run())
+ loop.run_until_complete(task)
+ finally:
+ loop.close()
+
+ def test_exception_chaining_after_await_with_context_cycle(self):
+ # Check trying to create an exception context cycle:
+ # https://bugs.python.org/issue40696
+ has_cycle = None
+ loop = asyncio.new_event_loop()
+ self.set_event_loop(loop)
+
+ async def process_exc(exc):
+ raise exc
+
+ async def run():
+ nonlocal has_cycle
+ try:
+ raise KeyError('a')
+ except Exception as exc:
+ task = self.new_task(loop, process_exc(exc))
+ try:
+ await task
+ except BaseException as exc:
+ has_cycle = (exc is exc.__context__)
+ # Prevent a hang if has_cycle is True.
+ exc.__context__ = None
+
+ try:
+ task = self.new_task(loop, run())
+ loop.run_until_complete(task)
+ finally:
+ loop.close()
+ # This also distinguishes from the initial has_cycle=None.
+ self.assertEqual(has_cycle, False)
def test_cancel(self):
diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py
index 87cc2df..bf48221 100644
--- a/Lib/test/test_generators.py
+++ b/Lib/test/test_generators.py
@@ -371,6 +371,32 @@ class GeneratorThrowTest(unittest.TestCase):
context = cm.exception.__context__
self.assertEqual((type(context), context.args), (KeyError, ('a',)))
+ def test_exception_context_with_yield_from_with_context_cycle(self):
+ # Check trying to create an exception context cycle:
+ # https://bugs.python.org/issue40696
+ has_cycle = None
+
+ def f():
+ yield
+
+ def g(exc):
+ nonlocal has_cycle
+ try:
+ raise exc
+ except Exception:
+ try:
+ yield from f()
+ except Exception as exc:
+ has_cycle = (exc is exc.__context__)
+ yield
+
+ exc = KeyError('a')
+ gen = g(exc)
+ gen.send(None)
+ gen.throw(exc)
+ # This also distinguishes from the initial has_cycle=None.
+ self.assertEqual(has_cycle, False)
+
def test_throw_after_none_exc_type(self):
def g():
try: