Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

gh-116303: Skip tests if C recursion limit is unavailable #117368

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions Lib/test/list_tests.py
Expand Up @@ -6,7 +6,7 @@
from functools import cmp_to_key

from test import seq_tests
from test.support import ALWAYS_EQ, NEVER_EQ, Py_C_RECURSION_LIMIT
from test.support import ALWAYS_EQ, NEVER_EQ, get_c_recursion_limit


class CommonTest(seq_tests.CommonTest):
Expand Down Expand Up @@ -61,7 +61,7 @@ def test_repr(self):

def test_repr_deep(self):
a = self.type2test([])
for i in range(Py_C_RECURSION_LIMIT + 1):
for i in range(get_c_recursion_limit() + 1):
a = self.type2test([a])
self.assertRaises(RecursionError, repr, a)

Expand Down
4 changes: 2 additions & 2 deletions Lib/test/mapping_tests.py
@@ -1,7 +1,7 @@
# tests common to dict and UserDict
import unittest
import collections
from test.support import Py_C_RECURSION_LIMIT
from test.support import get_c_recursion_limit


class BasicTestMappingProtocol(unittest.TestCase):
Expand Down Expand Up @@ -624,7 +624,7 @@ def __repr__(self):

def test_repr_deep(self):
d = self._empty_mapping()
for i in range(Py_C_RECURSION_LIMIT + 1):
for i in range(get_c_recursion_limit() + 1):
d0 = d
d = self._empty_mapping()
d[1] = d0
Expand Down
20 changes: 8 additions & 12 deletions Lib/test/support/__init__.py
Expand Up @@ -56,7 +56,7 @@
"run_with_tz", "PGO", "missing_compiler_executable",
"ALWAYS_EQ", "NEVER_EQ", "LARGEST", "SMALLEST",
"LOOPBACK_TIMEOUT", "INTERNET_TIMEOUT", "SHORT_TIMEOUT", "LONG_TIMEOUT",
"Py_DEBUG", "EXCEEDS_RECURSION_LIMIT", "Py_C_RECURSION_LIMIT",
"Py_DEBUG", "exceeds_recursion_limit", "get_c_recursion_limit",
"skip_on_s390x",
"without_optimizer",
]
Expand Down Expand Up @@ -2485,22 +2485,18 @@ def adjust_int_max_str_digits(max_digits):
sys.set_int_max_str_digits(current)


def _get_c_recursion_limit():
def get_c_recursion_limit():
try:
import _testcapi
return _testcapi.Py_C_RECURSION_LIMIT
except (ImportError, AttributeError):
# Originally taken from Include/cpython/pystate.h .
if sys.platform == 'win32':
return 4000
else:
return 10000
except ImportError:
raise unittest.SkipTest('requires _testcapi')

erlend-aasland marked this conversation as resolved.
Show resolved Hide resolved

# The default C recursion limit.
Py_C_RECURSION_LIMIT = _get_c_recursion_limit()
def exceeds_recursion_limit():
"""For recursion tests, easily exceeds default recursion limit."""
return get_c_recursion_limit() * 3

#For recursion tests, easily exceeds default recursion limit
EXCEEDS_RECURSION_LIMIT = Py_C_RECURSION_LIMIT * 3

#Windows doesn't have os.uname() but it doesn't support s390x.
skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x',
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_ast.py
Expand Up @@ -1153,9 +1153,9 @@ def next(self):

@support.cpython_only
def test_ast_recursion_limit(self):
fail_depth = support.EXCEEDS_RECURSION_LIMIT
fail_depth = support.exceeds_recursion_limit()
crash_depth = 100_000
success_depth = int(support.Py_C_RECURSION_LIMIT * 0.8)
success_depth = int(support.get_c_recursion_limit() * 0.8)
if _testinternalcapi is not None:
remaining = _testinternalcapi.get_c_recursion_remaining()
success_depth = min(success_depth, remaining)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_collections.py
Expand Up @@ -542,7 +542,7 @@ def test_odd_sizes(self):
self.assertEqual(Dot(1)._replace(d=999), (999,))
self.assertEqual(Dot(1)._fields, ('d',))

n = support.EXCEEDS_RECURSION_LIMIT
n = support.exceeds_recursion_limit()
names = list(set(''.join([choice(string.ascii_letters)
for j in range(10)]) for i in range(n)))
n = len(names)
Expand Down
11 changes: 6 additions & 5 deletions Lib/test/test_compile.py
Expand Up @@ -13,7 +13,7 @@
import warnings
from test import support
from test.support import (script_helper, requires_debug_ranges,
requires_specialization, Py_C_RECURSION_LIMIT)
requires_specialization, get_c_recursion_limit)
from test.support.bytecode_helper import instructions_with_positions
from test.support.os_helper import FakePath

Expand Down Expand Up @@ -114,7 +114,7 @@ def __getitem__(self, key):

@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
def test_extended_arg(self):
repeat = int(Py_C_RECURSION_LIMIT * 0.9)
repeat = int(get_c_recursion_limit() * 0.9)
longexpr = 'x = x or ' + '-x' * repeat
g = {}
code = textwrap.dedent('''
Expand Down Expand Up @@ -634,9 +634,10 @@ def test_yet_more_evil_still_undecodable(self):
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
def test_compiler_recursion_limit(self):
# Expected limit is Py_C_RECURSION_LIMIT
fail_depth = Py_C_RECURSION_LIMIT + 1
crash_depth = Py_C_RECURSION_LIMIT * 100
success_depth = int(Py_C_RECURSION_LIMIT * 0.8)
limit = get_c_recursion_limit()
fail_depth = limit + 1
crash_depth = limit * 100
success_depth = int(limit * 0.8)

def check_limit(prefix, repeated, mode="single"):
expect_ok = prefix + repeated * success_depth
Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_dict.py
Expand Up @@ -8,7 +8,7 @@
import unittest
import weakref
from test import support
from test.support import import_helper, Py_C_RECURSION_LIMIT
from test.support import import_helper, get_c_recursion_limit


class DictTest(unittest.TestCase):
Expand Down Expand Up @@ -596,7 +596,7 @@ def __repr__(self):

def test_repr_deep(self):
d = {}
for i in range(Py_C_RECURSION_LIMIT + 1):
for i in range(get_c_recursion_limit() + 1):
d = {1: d}
self.assertRaises(RecursionError, repr, d)

Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_dictviews.py
Expand Up @@ -2,7 +2,7 @@
import copy
import pickle
import unittest
from test.support import Py_C_RECURSION_LIMIT
from test.support import get_c_recursion_limit

class DictSetTest(unittest.TestCase):

Expand Down Expand Up @@ -279,7 +279,7 @@ def test_recursive_repr(self):

def test_deeply_nested_repr(self):
d = {}
for i in range(Py_C_RECURSION_LIMIT//2 + 100):
for i in range(get_c_recursion_limit()//2 + 100):
d = {42: d.values()}
self.assertRaises(RecursionError, repr, d)

Expand Down
4 changes: 2 additions & 2 deletions Lib/test/test_exception_group.py
@@ -1,7 +1,7 @@
import collections.abc
import types
import unittest
from test.support import Py_C_RECURSION_LIMIT
from test.support import get_c_recursion_limit

class TestExceptionGroupTypeHierarchy(unittest.TestCase):
def test_exception_group_types(self):
Expand Down Expand Up @@ -460,7 +460,7 @@ def test_basics_split_by_predicate__match(self):
class DeepRecursionInSplitAndSubgroup(unittest.TestCase):
def make_deep_eg(self):
e = TypeError(1)
for i in range(Py_C_RECURSION_LIMIT + 1):
for i in range(get_c_recursion_limit() + 1):
e = ExceptionGroup('eg', [e])
return e

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_exceptions.py
Expand Up @@ -1424,7 +1424,7 @@ def gen():
next(generator)
recursionlimit = sys.getrecursionlimit()
try:
recurse(support.EXCEEDS_RECURSION_LIMIT)
recurse(support.exceeds_recursion_limit())
finally:
sys.setrecursionlimit(recursionlimit)
print('Done.')
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_functools.py
Expand Up @@ -1867,7 +1867,7 @@ def fib(n):
return fib(n-1) + fib(n-2)

if not support.Py_DEBUG:
depth = support.Py_C_RECURSION_LIMIT*2//7
depth = support.get_c_recursion_limit()*2//7
with support.infinite_recursion():
fib(depth)
if self.module == c_functools:
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_isinstance.py
Expand Up @@ -352,7 +352,7 @@ def blowstack(fxn, arg, compare_to):
# Make sure that calling isinstance with a deeply nested tuple for its
# argument will raise RecursionError eventually.
tuple_arg = (compare_to,)
for cnt in range(support.EXCEEDS_RECURSION_LIMIT):
for cnt in range(support.exceeds_recursion_limit()):
tuple_arg = (tuple_arg,)
fxn(arg, tuple_arg)

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_marshal.py
Expand Up @@ -118,7 +118,7 @@ def test_code(self):
def test_many_codeobjects(self):
# Issue2957: bad recursion count on code objects
# more than MAX_MARSHAL_STACK_DEPTH
count = support.EXCEEDS_RECURSION_LIMIT
count = support.exceeds_recursion_limit()
codes = (ExceptionTestCase.test_exceptions.__code__,) * count
marshal.loads(marshal.dumps(codes))

Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_sys_settrace.py
Expand Up @@ -3039,7 +3039,7 @@ def test_trace_unpack_long_sequence(self):

def test_trace_lots_of_globals(self):

count = min(1000, int(support.Py_C_RECURSION_LIMIT * 0.8))
count = min(1000, int(support.get_c_recursion_limit() * 0.8))

code = """if 1:
def f():
Expand Down