Commit 00337094 authored by Jason Madden's avatar Jason Madden Committed by GitHub

Merge pull request #1716 from gevent/issue1704

More gracefully handle errors pickling a traceback.
parents ecfd9603 23d22f5c
Make error reporting when a greenlet suffers a `RecursionError` more
reliable.
Reported by Dan Milon.
......@@ -565,7 +565,12 @@ class Greenlet(greenlet):
"""
ei = self._exc_info
if ei is not None and ei[0] is not None:
return (ei[0], ei[1], load_traceback(ei[2]))
return (
ei[0],
ei[1],
# The pickled traceback may be None if we couldn't pickle it.
load_traceback(ei[2]) if ei[2] else None
)
def throw(self, *args):
"""Immediately switch into the greenlet and raise an exception in it.
......@@ -834,7 +839,13 @@ class Greenlet(greenlet):
self.__report_result(exc_info[1])
return
self._exc_info = exc_info[0], exc_info[1], dump_traceback(exc_info[2])
# Depending on the error, we may not be able to pickle it.
# In particular, RecursionError can be a problem.
try:
tb = dump_traceback(exc_info[2])
except: # pylint:disable=bare-except
tb = None
self._exc_info = exc_info[0], exc_info[1], tb
hub = get_my_hub(self) # pylint:disable=undefined-variable
if self._links and not self._notifier:
......
......@@ -598,6 +598,47 @@ class TestBasic(greentest.TestCase):
g.join()
self.assertFalse(g.exc_info)
def test_recursion_error(self):
# https://github.com/gevent/gevent/issues/1704
# A RuntimeError: recursion depth exceeded
# does not break things.
def recur():
recur() # This is expected to raise RecursionError
errors = []
def handle_error(glet, t, v, tb):
errors.append((glet, t, v, tb))
try:
gevent.get_hub().handle_error = handle_error
g = gevent.spawn(recur)
def wait():
return gevent.joinall([g])
g2 = gevent.spawn(wait)
gevent.joinall([g2])
finally:
del gevent.get_hub().handle_error
try:
expected_exc = RecursionError
except NameError:
expected_exc = RuntimeError
with self.assertRaises(expected_exc):
g.get()
self.assertFalse(g.successful())
self.assertTrue(g.dead)
self.assertTrue(errors)
self.assertEqual(1, len(errors))
self.assertIs(errors[0][0], g)
self.assertEqual(errors[0][1], expected_exc)
del errors[:]
def test_tree_locals(self):
g = g2 = None
def func():
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment