GH-91079: Rename C_RECURSION_LIMIT to Py_C_RECURSION_LIMIT (#108507)

Symbols of the C API should be prefixed by "Py_" to avoid conflict
with existing names in 3rd party C extensions on "#include <Python.h>".

test.pythoninfo now logs Py_C_RECURSION_LIMIT constant and other
_testcapi and _testinternalcapi constants.
This commit is contained in:
Victor Stinner 2023-09-08 11:48:28 +02:00 committed by GitHub
parent 15d4c9fabc
commit b0edf3b98e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 67 additions and 39 deletions

View File

@ -194,18 +194,17 @@ struct _ts {
}; };
/* WASI has limited call stack. Python's recursion limit depends on code #ifdef __wasi__
layout, optimization, and WASI runtime. Wasmtime can handle about 700 // WASI has limited call stack. Python's recursion limit depends on code
recursions, sometimes less. 500 is a more conservative limit. */ // layout, optimization, and WASI runtime. Wasmtime can handle about 700
#ifndef C_RECURSION_LIMIT // recursions, sometimes less. 500 is a more conservative limit.
# ifdef __wasi__ # define Py_C_RECURSION_LIMIT 500
# define C_RECURSION_LIMIT 500 #else
# else // This value is duplicated in Lib/test/support/__init__.py
// This value is duplicated in Lib/test/support/__init__.py # define Py_C_RECURSION_LIMIT 1500
# define C_RECURSION_LIMIT 1500
# endif
#endif #endif
/* other API */ /* other API */
/* Similar to PyThreadState_Get(), but don't issue a fatal error /* Similar to PyThreadState_Get(), but don't issue a fatal error

View File

@ -6,7 +6,7 @@ import sys
from functools import cmp_to_key from functools import cmp_to_key
from test import seq_tests from test import seq_tests
from test.support import ALWAYS_EQ, NEVER_EQ, C_RECURSION_LIMIT from test.support import ALWAYS_EQ, NEVER_EQ, Py_C_RECURSION_LIMIT
class CommonTest(seq_tests.CommonTest): class CommonTest(seq_tests.CommonTest):
@ -61,7 +61,7 @@ class CommonTest(seq_tests.CommonTest):
def test_repr_deep(self): def test_repr_deep(self):
a = self.type2test([]) a = self.type2test([])
for i in range(C_RECURSION_LIMIT + 1): for i in range(Py_C_RECURSION_LIMIT + 1):
a = self.type2test([a]) a = self.type2test([a])
self.assertRaises(RecursionError, repr, a) self.assertRaises(RecursionError, repr, a)

View File

@ -2,7 +2,7 @@
import unittest import unittest
import collections import collections
import sys import sys
from test.support import C_RECURSION_LIMIT from test.support import Py_C_RECURSION_LIMIT
class BasicTestMappingProtocol(unittest.TestCase): class BasicTestMappingProtocol(unittest.TestCase):
@ -625,7 +625,7 @@ class TestHashMappingProtocol(TestMappingProtocol):
def test_repr_deep(self): def test_repr_deep(self):
d = self._empty_mapping() d = self._empty_mapping()
for i in range(C_RECURSION_LIMIT + 1): for i in range(Py_C_RECURSION_LIMIT + 1):
d0 = d d0 = d
d = self._empty_mapping() d = self._empty_mapping()
d[1] = d0 d[1] = d0

View File

@ -665,6 +665,22 @@ def collect_decimal(info_add):
def collect_testcapi(info_add): def collect_testcapi(info_add):
try:
import _testcapi
except ImportError:
return
for name in (
'LONG_MAX', # always 32-bit on Windows, 64-bit on 64-bit Unix
'PY_SSIZE_T_MAX',
'Py_C_RECURSION_LIMIT',
'SIZEOF_TIME_T', # 32-bit or 64-bit depending on the platform
'SIZEOF_WCHAR_T', # 16-bit or 32-bit depending on the platform
):
copy_attr(info_add, f'_testcapi.{name}', _testcapi, name)
def collect_testinternalcapi(info_add):
try: try:
import _testinternalcapi import _testinternalcapi
except ImportError: except ImportError:
@ -672,6 +688,12 @@ def collect_testcapi(info_add):
call_func(info_add, 'pymem.allocator', _testinternalcapi, 'pymem_getallocatorsname') call_func(info_add, 'pymem.allocator', _testinternalcapi, 'pymem_getallocatorsname')
for name in (
'SIZEOF_PYGC_HEAD',
'SIZEOF_PYOBJECT',
):
copy_attr(info_add, f'_testinternalcapi.{name}', _testinternalcapi, name)
def collect_resource(info_add): def collect_resource(info_add):
try: try:
@ -907,6 +929,7 @@ def collect_info(info):
collect_sys, collect_sys,
collect_sysconfig, collect_sysconfig,
collect_testcapi, collect_testcapi,
collect_testinternalcapi,
collect_time, collect_time,
collect_tkinter, collect_tkinter,
collect_windows, collect_windows,

View File

@ -60,7 +60,7 @@ __all__ = [
"run_with_tz", "PGO", "missing_compiler_executable", "run_with_tz", "PGO", "missing_compiler_executable",
"ALWAYS_EQ", "NEVER_EQ", "LARGEST", "SMALLEST", "ALWAYS_EQ", "NEVER_EQ", "LARGEST", "SMALLEST",
"LOOPBACK_TIMEOUT", "INTERNET_TIMEOUT", "SHORT_TIMEOUT", "LONG_TIMEOUT", "LOOPBACK_TIMEOUT", "INTERNET_TIMEOUT", "SHORT_TIMEOUT", "LONG_TIMEOUT",
"Py_DEBUG", "EXCEEDS_RECURSION_LIMIT", "C_RECURSION_LIMIT", "Py_DEBUG", "EXCEEDS_RECURSION_LIMIT", "Py_C_RECURSION_LIMIT",
"skip_on_s390x", "skip_on_s390x",
] ]
@ -2531,7 +2531,7 @@ def adjust_int_max_str_digits(max_digits):
EXCEEDS_RECURSION_LIMIT = 5000 EXCEEDS_RECURSION_LIMIT = 5000
# The default C recursion limit (from Include/cpython/pystate.h). # The default C recursion limit (from Include/cpython/pystate.h).
C_RECURSION_LIMIT = 1500 Py_C_RECURSION_LIMIT = 1500
#Windows doesn't have os.uname() but it doesn't support s390x. #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', skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x',

View File

@ -11,7 +11,7 @@ import textwrap
import warnings import warnings
from test import support from test import support
from test.support import (script_helper, requires_debug_ranges, from test.support import (script_helper, requires_debug_ranges,
requires_specialization, C_RECURSION_LIMIT) requires_specialization, Py_C_RECURSION_LIMIT)
from test.support.os_helper import FakePath from test.support.os_helper import FakePath
class TestSpecifics(unittest.TestCase): class TestSpecifics(unittest.TestCase):
@ -111,7 +111,7 @@ class TestSpecifics(unittest.TestCase):
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI") @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
def test_extended_arg(self): def test_extended_arg(self):
repeat = int(C_RECURSION_LIMIT * 0.9) repeat = int(Py_C_RECURSION_LIMIT * 0.9)
longexpr = 'x = x or ' + '-x' * repeat longexpr = 'x = x or ' + '-x' * repeat
g = {} g = {}
code = textwrap.dedent(''' code = textwrap.dedent('''
@ -557,12 +557,12 @@ class TestSpecifics(unittest.TestCase):
@support.cpython_only @support.cpython_only
@unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI") @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI")
def test_compiler_recursion_limit(self): def test_compiler_recursion_limit(self):
# Expected limit is C_RECURSION_LIMIT * 2 # Expected limit is Py_C_RECURSION_LIMIT * 2
# Duplicating the limit here is a little ugly. # Duplicating the limit here is a little ugly.
# Perhaps it should be exposed somewhere... # Perhaps it should be exposed somewhere...
fail_depth = C_RECURSION_LIMIT * 2 + 1 fail_depth = Py_C_RECURSION_LIMIT * 2 + 1
crash_depth = C_RECURSION_LIMIT * 100 crash_depth = Py_C_RECURSION_LIMIT * 100
success_depth = int(C_RECURSION_LIMIT * 1.8) success_depth = int(Py_C_RECURSION_LIMIT * 1.8)
def check_limit(prefix, repeated, mode="single"): def check_limit(prefix, repeated, mode="single"):
expect_ok = prefix + repeated * success_depth expect_ok = prefix + repeated * success_depth

View File

@ -8,7 +8,7 @@ import sys
import unittest import unittest
import weakref import weakref
from test import support from test import support
from test.support import import_helper, C_RECURSION_LIMIT from test.support import import_helper, Py_C_RECURSION_LIMIT
class DictTest(unittest.TestCase): class DictTest(unittest.TestCase):
@ -596,7 +596,7 @@ class DictTest(unittest.TestCase):
def test_repr_deep(self): def test_repr_deep(self):
d = {} d = {}
for i in range(C_RECURSION_LIMIT + 1): for i in range(Py_C_RECURSION_LIMIT + 1):
d = {1: d} d = {1: d}
self.assertRaises(RecursionError, repr, d) self.assertRaises(RecursionError, repr, d)

View File

@ -3,7 +3,7 @@ import copy
import pickle import pickle
import sys import sys
import unittest import unittest
from test.support import C_RECURSION_LIMIT from test.support import Py_C_RECURSION_LIMIT
class DictSetTest(unittest.TestCase): class DictSetTest(unittest.TestCase):
@ -280,7 +280,7 @@ class DictSetTest(unittest.TestCase):
def test_deeply_nested_repr(self): def test_deeply_nested_repr(self):
d = {} d = {}
for i in range(C_RECURSION_LIMIT//2 + 100): for i in range(Py_C_RECURSION_LIMIT//2 + 100):
d = {42: d.values()} d = {42: d.values()}
self.assertRaises(RecursionError, repr, d) self.assertRaises(RecursionError, repr, d)

View File

@ -1,7 +1,7 @@
import collections.abc import collections.abc
import types import types
import unittest import unittest
from test.support import C_RECURSION_LIMIT from test.support import Py_C_RECURSION_LIMIT
class TestExceptionGroupTypeHierarchy(unittest.TestCase): class TestExceptionGroupTypeHierarchy(unittest.TestCase):
def test_exception_group_types(self): def test_exception_group_types(self):
@ -460,7 +460,7 @@ class ExceptionGroupSplitTests(ExceptionGroupTestBase):
class DeepRecursionInSplitAndSubgroup(unittest.TestCase): class DeepRecursionInSplitAndSubgroup(unittest.TestCase):
def make_deep_eg(self): def make_deep_eg(self):
e = TypeError(1) e = TypeError(1)
for i in range(C_RECURSION_LIMIT + 1): for i in range(Py_C_RECURSION_LIMIT + 1):
e = ExceptionGroup('eg', [e]) e = ExceptionGroup('eg', [e])
return e return e

View File

@ -3922,6 +3922,7 @@ PyInit__testcapi(void)
PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type); PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type);
PyModule_AddIntConstant(m, "the_number_three", 3); PyModule_AddIntConstant(m, "the_number_three", 3);
PyModule_AddIntMacro(m, Py_C_RECURSION_LIMIT);
TestError = PyErr_NewException("_testcapi.error", NULL, NULL); TestError = PyErr_NewException("_testcapi.error", NULL, NULL);
Py_INCREF(TestError); Py_INCREF(TestError);

View File

@ -1552,6 +1552,11 @@ module_exec(PyObject *module)
return 1; return 1;
} }
if (PyModule_Add(module, "SIZEOF_PYOBJECT",
PyLong_FromSsize_t(sizeof(PyObject))) < 0) {
return 1;
}
if (PyModule_Add(module, "SIZEOF_TIME_T", if (PyModule_Add(module, "SIZEOF_TIME_T",
PyLong_FromSsize_t(sizeof(time_t))) < 0) { PyLong_FromSsize_t(sizeof(time_t))) < 0) {
return 1; return 1;

View File

@ -1401,8 +1401,8 @@ PyObject* PyAST_mod2obj(mod_ty t)
if (!tstate) { if (!tstate) {
return 0; return 0;
} }
state->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; state->recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
state->recursion_depth = starting_recursion_depth; state->recursion_depth = starting_recursion_depth;

4
Python/Python-ast.c generated
View File

@ -13081,8 +13081,8 @@ PyObject* PyAST_mod2obj(mod_ty t)
if (!tstate) { if (!tstate) {
return 0; return 0;
} }
state->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; state->recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
state->recursion_depth = starting_recursion_depth; state->recursion_depth = starting_recursion_depth;

View File

@ -1046,10 +1046,10 @@ _PyAST_Validate(mod_ty mod)
return 0; return 0;
} }
/* Be careful here to prevent overflow. */ /* Be careful here to prevent overflow. */
int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
state.recursion_depth = starting_recursion_depth; state.recursion_depth = starting_recursion_depth;
state.recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; state.recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
switch (mod->kind) { switch (mod->kind) {
case Module_kind: case Module_kind:

View File

@ -1130,10 +1130,10 @@ _PyAST_Optimize(mod_ty mod, PyArena *arena, int optimize, int ff_features)
return 0; return 0;
} }
/* Be careful here to prevent overflow. */ /* Be careful here to prevent overflow. */
int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
state.recursion_depth = starting_recursion_depth; state.recursion_depth = starting_recursion_depth;
state.recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; state.recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
int ret = astfold_mod(mod, arena, &state); int ret = astfold_mod(mod, arena, &state);
assert(ret || PyErr_Occurred()); assert(ret || PyErr_Occurred());

View File

@ -1334,7 +1334,7 @@ init_threadstate(PyThreadState *tstate,
tstate->py_recursion_limit = interp->ceval.recursion_limit, tstate->py_recursion_limit = interp->ceval.recursion_limit,
tstate->py_recursion_remaining = interp->ceval.recursion_limit, tstate->py_recursion_remaining = interp->ceval.recursion_limit,
tstate->c_recursion_remaining = C_RECURSION_LIMIT; tstate->c_recursion_remaining = Py_C_RECURSION_LIMIT;
tstate->exc_info = &tstate->exc_state; tstate->exc_info = &tstate->exc_state;

View File

@ -312,10 +312,10 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future)
return NULL; return NULL;
} }
/* Be careful here to prevent overflow. */ /* Be careful here to prevent overflow. */
int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining;
starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE;
st->recursion_depth = starting_recursion_depth; st->recursion_depth = starting_recursion_depth;
st->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; st->recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE;
/* Make the initial symbol information gathering pass */ /* Make the initial symbol information gathering pass */
if (!symtable_enter_block(st, &_Py_ID(top), ModuleBlock, (void *)mod, 0, 0, 0, 0)) { if (!symtable_enter_block(st, &_Py_ID(top), ModuleBlock, (void *)mod, 0, 0, 0, 0)) {