Replaces the default exception hook with one that, upon "infinite recursion", removes the last cycle. This results in a significantly cleaner and shorter error message.
Usage: simply import <module> as _
For more details see the descussion here: https://mail.python.org/pipermail/python-ideas/2013-September/023190.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | '''
@since: 15/09/2013
@version: 0.3 06/07/2015
@author: Elazar Gershuni (elazarg at gmail)
'''
import sys
import traceback
def _find_last_cycle(tb):
call_names = set()
size = 0
for i, call in enumerate(reversed(tb)):
#call is not hashable in python3.5, but it has a name
name = getattr(call, 'name', None) or hash(call)
if size == 0:
call_names.add(name)
if call == tb[-1]:
size = i
elif name not in call_names:
length = i
break
return size, length
def is_recursion_error(exctype, trace):
if exctype.__name__ == 'RecursionError':
return True
if exctype is not RuntimeError:
return False
# prior to python3.5 there's no RecursionError,
# so we check the size
tb = traceback.extract_tb(trace)
return len(tb) == sys.getrecursionlimit()
def cycle_detect_excepthook(exctype, value, trace):
if is_recursion_error(exctype, trace):
tb = traceback.extract_tb(trace)
size, length = _find_last_cycle(tb)
count = round(length / size, 2)
if count >= 2:
traceback.print_exception(exctype, value, trace, len(tb) - length + size)
# sadly, we have no standard way to add this line to the real error stream
sys.stderr.write('{} occurrences of cycle of size {} detected\n'
.format(count, size))
return
sys.__excepthook__(exctype, value, trace)
# the excepthook
sys.excepthook = cycle_detect_excepthook
if __name__ == '__main__':
def p2(): p0()
def p1(): p2()
def p0(): p1()
def bar():
p0()
bar()
'''
Output in interactive mode:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in bar
File "<stdin>", line 1, in p0
File "<stdin>", line 1, in p1
File "<stdin>", line 1, in p2
RuntimeError: maximum recursion depth exceeded
332.67 occurrences of cycle of size 3 detected
'''
|
I would like to hear about
- Better ways to detect/report cycles.
- A way to print the results to the correct output (which may not be sys.stderr).
A discussion in https://mail.python.org/mailman/listinfo/python-ideas