From b0edf3b98e4b3e68a13776e034b9dd86ad7e529d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 8 Sep 2023 11:48:28 +0200 Subject: [PATCH] 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 ". test.pythoninfo now logs Py_C_RECURSION_LIMIT constant and other _testcapi and _testinternalcapi constants. --- Include/cpython/pystate.h | 19 +++++++++---------- Lib/test/list_tests.py | 4 ++-- Lib/test/mapping_tests.py | 4 ++-- Lib/test/pythoninfo.py | 23 +++++++++++++++++++++++ Lib/test/support/__init__.py | 4 ++-- Lib/test/test_compile.py | 12 ++++++------ Lib/test/test_dict.py | 4 ++-- Lib/test/test_dictviews.py | 4 ++-- Lib/test/test_exception_group.py | 4 ++-- Modules/_testcapimodule.c | 1 + Modules/_testinternalcapi.c | 5 +++++ Parser/asdl_c.py | 4 ++-- Python/Python-ast.c | 4 ++-- Python/ast.c | 4 ++-- Python/ast_opt.c | 4 ++-- Python/pystate.c | 2 +- Python/symtable.c | 4 ++-- 17 files changed, 67 insertions(+), 39 deletions(-) diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index fc5f58db86d..e1a15cddd3d 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -194,18 +194,17 @@ struct _ts { }; -/* WASI has limited call stack. Python's recursion limit depends on code - layout, optimization, and WASI runtime. Wasmtime can handle about 700 - recursions, sometimes less. 500 is a more conservative limit. */ -#ifndef C_RECURSION_LIMIT -# ifdef __wasi__ -# define C_RECURSION_LIMIT 500 -# else - // This value is duplicated in Lib/test/support/__init__.py -# define C_RECURSION_LIMIT 1500 -# endif +#ifdef __wasi__ + // WASI has limited call stack. Python's recursion limit depends on code + // layout, optimization, and WASI runtime. Wasmtime can handle about 700 + // recursions, sometimes less. 500 is a more conservative limit. +# define Py_C_RECURSION_LIMIT 500 +#else + // This value is duplicated in Lib/test/support/__init__.py +# define Py_C_RECURSION_LIMIT 1500 #endif + /* other API */ /* Similar to PyThreadState_Get(), but don't issue a fatal error diff --git a/Lib/test/list_tests.py b/Lib/test/list_tests.py index b1ef332522d..d9ab21d4941 100644 --- a/Lib/test/list_tests.py +++ b/Lib/test/list_tests.py @@ -6,7 +6,7 @@ import sys from functools import cmp_to_key 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): @@ -61,7 +61,7 @@ class CommonTest(seq_tests.CommonTest): def test_repr_deep(self): a = self.type2test([]) - for i in range(C_RECURSION_LIMIT + 1): + for i in range(Py_C_RECURSION_LIMIT + 1): a = self.type2test([a]) self.assertRaises(RecursionError, repr, a) diff --git a/Lib/test/mapping_tests.py b/Lib/test/mapping_tests.py index 5492bbf86d1..b3e4192e65d 100644 --- a/Lib/test/mapping_tests.py +++ b/Lib/test/mapping_tests.py @@ -2,7 +2,7 @@ import unittest import collections import sys -from test.support import C_RECURSION_LIMIT +from test.support import Py_C_RECURSION_LIMIT class BasicTestMappingProtocol(unittest.TestCase): @@ -625,7 +625,7 @@ class TestHashMappingProtocol(TestMappingProtocol): def test_repr_deep(self): d = self._empty_mapping() - for i in range(C_RECURSION_LIMIT + 1): + for i in range(Py_C_RECURSION_LIMIT + 1): d0 = d d = self._empty_mapping() d[1] = d0 diff --git a/Lib/test/pythoninfo.py b/Lib/test/pythoninfo.py index c6288334780..b25def78e42 100644 --- a/Lib/test/pythoninfo.py +++ b/Lib/test/pythoninfo.py @@ -665,6 +665,22 @@ def collect_decimal(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: import _testinternalcapi except ImportError: @@ -672,6 +688,12 @@ def collect_testcapi(info_add): 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): try: @@ -907,6 +929,7 @@ def collect_info(info): collect_sys, collect_sysconfig, collect_testcapi, + collect_testinternalcapi, collect_time, collect_tkinter, collect_windows, diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index 38ad965e155..84b74ee2c29 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -60,7 +60,7 @@ __all__ = [ "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", "C_RECURSION_LIMIT", + "Py_DEBUG", "EXCEEDS_RECURSION_LIMIT", "Py_C_RECURSION_LIMIT", "skip_on_s390x", ] @@ -2531,7 +2531,7 @@ def adjust_int_max_str_digits(max_digits): EXCEEDS_RECURSION_LIMIT = 5000 # 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. skip_on_s390x = unittest.skipIf(hasattr(os, 'uname') and os.uname().machine == 's390x', diff --git a/Lib/test/test_compile.py b/Lib/test/test_compile.py index de513daf825..28b2c4686bb 100644 --- a/Lib/test/test_compile.py +++ b/Lib/test/test_compile.py @@ -11,7 +11,7 @@ import textwrap import warnings from test import support 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 class TestSpecifics(unittest.TestCase): @@ -111,7 +111,7 @@ class TestSpecifics(unittest.TestCase): @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI") 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 g = {} code = textwrap.dedent(''' @@ -557,12 +557,12 @@ class TestSpecifics(unittest.TestCase): @support.cpython_only @unittest.skipIf(support.is_wasi, "exhausts limited stack on WASI") 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. # Perhaps it should be exposed somewhere... - fail_depth = C_RECURSION_LIMIT * 2 + 1 - crash_depth = C_RECURSION_LIMIT * 100 - success_depth = int(C_RECURSION_LIMIT * 1.8) + fail_depth = Py_C_RECURSION_LIMIT * 2 + 1 + crash_depth = Py_C_RECURSION_LIMIT * 100 + success_depth = int(Py_C_RECURSION_LIMIT * 1.8) def check_limit(prefix, repeated, mode="single"): expect_ok = prefix + repeated * success_depth diff --git a/Lib/test/test_dict.py b/Lib/test/test_dict.py index eab64b4f910..620d0ca4f4c 100644 --- a/Lib/test/test_dict.py +++ b/Lib/test/test_dict.py @@ -8,7 +8,7 @@ import sys import unittest import weakref 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): @@ -596,7 +596,7 @@ class DictTest(unittest.TestCase): def test_repr_deep(self): d = {} - for i in range(C_RECURSION_LIMIT + 1): + for i in range(Py_C_RECURSION_LIMIT + 1): d = {1: d} self.assertRaises(RecursionError, repr, d) diff --git a/Lib/test/test_dictviews.py b/Lib/test/test_dictviews.py index 2bd9d6eef8c..34918585513 100644 --- a/Lib/test/test_dictviews.py +++ b/Lib/test/test_dictviews.py @@ -3,7 +3,7 @@ import copy import pickle import sys import unittest -from test.support import C_RECURSION_LIMIT +from test.support import Py_C_RECURSION_LIMIT class DictSetTest(unittest.TestCase): @@ -280,7 +280,7 @@ class DictSetTest(unittest.TestCase): def test_deeply_nested_repr(self): d = {} - for i in range(C_RECURSION_LIMIT//2 + 100): + for i in range(Py_C_RECURSION_LIMIT//2 + 100): d = {42: d.values()} self.assertRaises(RecursionError, repr, d) diff --git a/Lib/test/test_exception_group.py b/Lib/test/test_exception_group.py index a02d54da35e..20122679223 100644 --- a/Lib/test/test_exception_group.py +++ b/Lib/test/test_exception_group.py @@ -1,7 +1,7 @@ import collections.abc import types import unittest -from test.support import C_RECURSION_LIMIT +from test.support import Py_C_RECURSION_LIMIT class TestExceptionGroupTypeHierarchy(unittest.TestCase): def test_exception_group_types(self): @@ -460,7 +460,7 @@ class ExceptionGroupSplitTests(ExceptionGroupTestBase): class DeepRecursionInSplitAndSubgroup(unittest.TestCase): def make_deep_eg(self): e = TypeError(1) - for i in range(C_RECURSION_LIMIT + 1): + for i in range(Py_C_RECURSION_LIMIT + 1): e = ExceptionGroup('eg', [e]) return e diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 9bd963baf06..85d8401435e 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -3922,6 +3922,7 @@ PyInit__testcapi(void) PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type); PyModule_AddIntConstant(m, "the_number_three", 3); + PyModule_AddIntMacro(m, Py_C_RECURSION_LIMIT); TestError = PyErr_NewException("_testcapi.error", NULL, NULL); Py_INCREF(TestError); diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index b6792e38fa9..922672d1a9f 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -1552,6 +1552,11 @@ module_exec(PyObject *module) return 1; } + if (PyModule_Add(module, "SIZEOF_PYOBJECT", + PyLong_FromSsize_t(sizeof(PyObject))) < 0) { + return 1; + } + if (PyModule_Add(module, "SIZEOF_TIME_T", PyLong_FromSsize_t(sizeof(time_t))) < 0) { return 1; diff --git a/Parser/asdl_c.py b/Parser/asdl_c.py index ca259c8cd1f..f61099b9705 100755 --- a/Parser/asdl_c.py +++ b/Parser/asdl_c.py @@ -1401,8 +1401,8 @@ PyObject* PyAST_mod2obj(mod_ty t) if (!tstate) { return 0; } - state->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; - int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; + state->recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; + int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining; starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; state->recursion_depth = starting_recursion_depth; diff --git a/Python/Python-ast.c b/Python/Python-ast.c index 77b23f7c5ed..a197d44868b 100644 --- a/Python/Python-ast.c +++ b/Python/Python-ast.c @@ -13081,8 +13081,8 @@ PyObject* PyAST_mod2obj(mod_ty t) if (!tstate) { return 0; } - state->recursion_limit = C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; - int recursion_depth = C_RECURSION_LIMIT - tstate->c_recursion_remaining; + state->recursion_limit = Py_C_RECURSION_LIMIT * COMPILER_STACK_FRAME_SCALE; + int recursion_depth = Py_C_RECURSION_LIMIT - tstate->c_recursion_remaining; starting_recursion_depth = recursion_depth * COMPILER_STACK_FRAME_SCALE; state->recursion_depth = starting_recursion_depth; diff --git a/Python/ast.c b/Python/ast.c index 74c97f948d1..21cb38f8cbf 100644 --- a/Python/ast.c +++ b/Python/ast.c @@ -1046,10 +1046,10 @@ _PyAST_Validate(mod_ty mod) return 0; } /* 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; 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) { case Module_kind: diff --git a/Python/ast_opt.c b/Python/ast_opt.c index 82e7559e5b6..41f48eba08a 100644 --- a/Python/ast_opt.c +++ b/Python/ast_opt.c @@ -1130,10 +1130,10 @@ _PyAST_Optimize(mod_ty mod, PyArena *arena, int optimize, int ff_features) return 0; } /* 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; 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); assert(ret || PyErr_Occurred()); diff --git a/Python/pystate.c b/Python/pystate.c index ed14f82688f..89275fd7e02 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -1334,7 +1334,7 @@ init_threadstate(PyThreadState *tstate, tstate->py_recursion_limit = 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; diff --git a/Python/symtable.c b/Python/symtable.c index f157d4c1703..217e6f59a61 100644 --- a/Python/symtable.c +++ b/Python/symtable.c @@ -312,10 +312,10 @@ _PySymtable_Build(mod_ty mod, PyObject *filename, PyFutureFeatures *future) return NULL; } /* 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; 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 */ if (!symtable_enter_block(st, &_Py_ID(top), ModuleBlock, (void *)mod, 0, 0, 0, 0)) {