1 #! /usr/bin/env python3
2 """Find the maximum recursion limit that prevents interpreter termination.
3
4 This script finds the maximum safe recursion limit on a particular
5 platform. If you need to change the recursion limit on your system,
6 this script will tell you a safe upper bound. To use the new limit,
7 call sys.setrecursionlimit().
8
9 This module implements several ways to create infinite recursion in
10 Python. Different implementations end up pushing different numbers of
11 C stack frames, depending on how many calls through Python's abstract
12 C API occur.
13
14 After each round of tests, it prints a message:
15 "Limit of NNNN is fine".
16
17 The highest printed value of "NNNN" is therefore the highest potentially
18 safe limit for your system (which depends on the OS, architecture, but also
19 the compilation flags). Please note that it is practically impossible to
20 test all possible recursion paths in the interpreter, so the results of
21 this test should not be trusted blindly -- although they give a good hint
22 of which values are reasonable.
23
24 NOTE: When the C stack space allocated by your system is exceeded due
25 to excessive recursion, exact behaviour depends on the platform, although
26 the interpreter will always fail in a likely brutal way: either a
27 segmentation fault, a MemoryError, or just a silent abort.
28
29 NB: A program that does not use __methods__ can set a higher limit.
30 """
31
32 import sys
33 import itertools
34
35 class ESC[4;38;5;81mRecursiveBlowup1:
36 def __init__(self):
37 self.__init__()
38
39 def test_init():
40 return RecursiveBlowup1()
41
42 class ESC[4;38;5;81mRecursiveBlowup2:
43 def __repr__(self):
44 return repr(self)
45
46 def test_repr():
47 return repr(RecursiveBlowup2())
48
49 class ESC[4;38;5;81mRecursiveBlowup4:
50 def __add__(self, x):
51 return x + self
52
53 def test_add():
54 return RecursiveBlowup4() + RecursiveBlowup4()
55
56 class ESC[4;38;5;81mRecursiveBlowup5:
57 def __getattr__(self, attr):
58 return getattr(self, attr)
59
60 def test_getattr():
61 return RecursiveBlowup5().attr
62
63 class ESC[4;38;5;81mRecursiveBlowup6:
64 def __getitem__(self, item):
65 return self[item - 2] + self[item - 1]
66
67 def test_getitem():
68 return RecursiveBlowup6()[5]
69
70 def test_recurse():
71 return test_recurse()
72
73 def test_cpickle(_cache={}):
74 import io
75 try:
76 import _pickle
77 except ImportError:
78 print("cannot import _pickle, skipped!")
79 return
80 k, l = None, None
81 for n in itertools.count():
82 try:
83 l = _cache[n]
84 continue # Already tried and it works, let's save some time
85 except KeyError:
86 for i in range(100):
87 l = [k, l]
88 k = {i: l}
89 _pickle.Pickler(io.BytesIO(), protocol=-1).dump(l)
90 _cache[n] = l
91
92 def test_compiler_recursion():
93 # The compiler uses a scaling factor to support additional levels
94 # of recursion. This is a sanity check of that scaling to ensure
95 # it still raises RecursionError even at higher recursion limits
96 compile("()" * (10 * sys.getrecursionlimit()), "<single>", "single")
97
98 def check_limit(n, test_func_name):
99 sys.setrecursionlimit(n)
100 if test_func_name.startswith("test_"):
101 print(test_func_name[5:])
102 else:
103 print(test_func_name)
104 test_func = globals()[test_func_name]
105 try:
106 test_func()
107 # AttributeError can be raised because of the way e.g. PyDict_GetItem()
108 # silences all exceptions and returns NULL, which is usually interpreted
109 # as "missing attribute".
110 except (RecursionError, AttributeError):
111 pass
112 else:
113 print("Yikes!")
114
115 if __name__ == '__main__':
116
117 limit = 1000
118 while 1:
119 check_limit(limit, "test_recurse")
120 check_limit(limit, "test_add")
121 check_limit(limit, "test_repr")
122 check_limit(limit, "test_init")
123 check_limit(limit, "test_getattr")
124 check_limit(limit, "test_getitem")
125 check_limit(limit, "test_cpickle")
126 check_limit(limit, "test_compiler_recursion")
127 print("Limit of %d is fine" % limit)
128 limit = limit + 100