PageRenderTime 62ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 1ms

/Tools/scripts/find_recursionlimit.py

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