From da4b1080e0dceaf7a7150a5a52f415303840ba64 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 16 Oct 2026 12:20:48 +0200 Subject: [PATCH] Revert "gh-158445: Allocate memory in the heap in Py_GetVersion() (#158608)" This reverts commit f5e913c7f710794421e48437f45a65f190830cbf. --- Doc/c-api/interp-lifecycle.rst | 2 +- Include/internal/pycore_pylifecycle.h | 3 +- Lib/test/test_capi/test_build.py | 8 +++- Modules/main.c | 1 - Python/getcompiler.c | 9 ++-- Python/getversion.c | 67 +++++---------------------- Python/pylifecycle.c | 6 +-- Tools/c-analyzer/cpython/ignored.tsv | 3 +- 8 files changed, 25 insertions(+), 74 deletions(-) diff --git a/Doc/c-api/interp-lifecycle.rst b/Doc/c-api/interp-lifecycle.rst index a43298d44bdc1c..7cfde5cdae6c56 100644 --- a/Doc/c-api/interp-lifecycle.rst +++ b/Doc/c-api/interp-lifecycle.rst @@ -514,7 +514,7 @@ Process-wide parameters Return the version of this Python interpreter. This is a string that looks something like :: - "3.15.0rc2 (3.15.0~rc2-1.fc44.x86_64, Sep 3 2026, 00:00:00) [GCC 16.2.1 20260819 (Red Hat 16.2.1-2)]" + "3.0a5+ (py3k:63103M, May 12 2008, 00:53:55) \n[GCC 4.2.3]" .. index:: single: version (in module sys) diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 99d09c5e0d4e11..bfc94e3e8529b7 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -23,7 +23,7 @@ extern int _Py_IsLocaleCoercionTarget(const char *ctype_loc); /* Various one-time initializers */ -extern PyStatus _Py_GetVersion_Init(void); +extern void _Py_InitVersion(void); extern PyStatus _PyFaulthandler_Init(int enable); extern PyObject * _PyBuiltin_Init(PyInterpreterState *interp); extern int _PyBuiltin_InitPythonFunctions(PyObject *dict); @@ -43,7 +43,6 @@ extern PyStatus _PyDateTime_InitTypes(PyInterpreterState *interp); /* Various internal finalizers */ -extern void _Py_GetVersion_Fini(void); extern int _PySignal_Init(int install_signal_handlers); extern void _PySignal_Fini(void); diff --git a/Lib/test/test_capi/test_build.py b/Lib/test/test_capi/test_build.py index d45c9dbc1c5882..8319ad1ec77da8 100644 --- a/Lib/test/test_capi/test_build.py +++ b/Lib/test/test_capi/test_build.py @@ -20,11 +20,15 @@ def test_getcopyright(self): # Test Py_GetCompiler() def test_getcompiler(self): - self.assertIn(_testlimitedcapi.py_getcompiler(), sys.version) + compiler = _testlimitedcapi.py_getcompiler() + self.assertIsInstance(compiler, str) + self.assertNotEqual(compiler, '') # Test Py_GetBuildInfo() def test_getbuildinfo(self): - self.assertIn(_testlimitedcapi.py_getbuildinfo(), sys.version) + buildinfo = _testlimitedcapi.py_getbuildinfo() + self.assertIsInstance(buildinfo, str) + self.assertNotEqual(buildinfo, '') # Test internal _Py_GetBuiltWithAssert() def test__getbuiltwithassert(self): diff --git a/Modules/main.c b/Modules/main.c index 097c10504d69a3..44a025d500e35e 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -847,7 +847,6 @@ pymain_free(void) _PyPathConfig_ClearGlobal(); _Py_ClearArgcArgv(); _PyRuntime_Finalize(); - _Py_GetVersion_Fini(); } diff --git a/Python/getcompiler.c b/Python/getcompiler.c index baa03679d79fb8..cc56ad8c895551 100644 --- a/Python/getcompiler.c +++ b/Python/getcompiler.c @@ -3,7 +3,6 @@ #include "Python.h" -// PC/pyconfig.h defines _Py_COMPILER #ifdef _Py_COMPILER # define COMPILER _Py_COMPILER #endif @@ -13,14 +12,14 @@ // Note the __clang__ conditional has to come before the __GNUC__ one because // clang pretends to be GCC. #if defined(__clang__) -# define COMPILER "[Clang " __clang_version__ "]" +#define COMPILER "[Clang " __clang_version__ "]" #elif defined(__GNUC__) -# define COMPILER "[GCC " __VERSION__ "]" +#define COMPILER "[GCC " __VERSION__ "]" // Generic fallbacks. #elif defined(__cplusplus) -# define COMPILER "[C++]" +#define COMPILER "[C++]" #else -# define COMPILER "[C]" +#define COMPILER "[C]" #endif #endif /* !COMPILER */ diff --git a/Python/getversion.c b/Python/getversion.c index 5c05b841de6fc8..8d8bc6ea70048c 100644 --- a/Python/getversion.c +++ b/Python/getversion.c @@ -2,77 +2,32 @@ /* Return the full version string. */ #include "Python.h" -#include "pycore_initconfig.h" // _PyStatus_NO_MEMORY() +#include "patchlevel.h" static int initialized = 0; -// Use the smallest possible buffer: it's only needed if malloc() fails *and* -// Py_GetVersion() is called before Py_Initialize(). The buffer should be big -// enough to store "3.16.0a0 " string. -static char static_version[20]; -static char *heap_version = NULL; +static char version[300]; - -PyStatus -_Py_GetVersion_Init(void) +void _Py_InitVersion(void) { if (initialized) { - return _PyStatus_OK(); + return; } - + initialized = 1; #ifdef Py_GIL_DISABLED - const char *format = "%s free-threading build (%s) %s"; - size_t format_len = strlen(" free-threading build () "); + const char *buildinfo_format = "%.80s free-threading build (%.80s) %.80s"; #else - const char *format = "%s (%s) %s"; - size_t format_len = strlen(" () "); + const char *buildinfo_format = "%.80s (%.80s) %.80s"; #endif - const char *version_str = PY_VERSION; - const char *buildinfo = Py_GetBuildInfo(); - const char *compiler = Py_GetCompiler(); - // +1 for the trailing NUL byte - size_t len = (format_len + strlen(version_str) + strlen(buildinfo) - + strlen(compiler) + 1); - - // Always format the static version - PyOS_snprintf(static_version, sizeof(static_version), format, - version_str, buildinfo, compiler); - - heap_version = malloc(len); - if (heap_version == NULL) { - // If malloc() failed, don't set initialized to 1, so next - // Py_GetVersion() will try again to allocate memory. - return _PyStatus_NO_MEMORY(); - } - - PyOS_snprintf(heap_version, len, format, - version_str, buildinfo, compiler); - initialized = 1; - return _PyStatus_OK(); -} - -void -_Py_GetVersion_Fini(void) -{ - if (heap_version) { - free(heap_version); - heap_version = NULL; - } + PyOS_snprintf(version, sizeof(version), buildinfo_format, + PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler()); } const char * Py_GetVersion(void) { - PyStatus status = _Py_GetVersion_Init(); - // Ignore error: Py_GetVersion() API cannot report error - (void)status; - - if (heap_version) { - return heap_version; - } - else { - return static_version; - } + _Py_InitVersion(); + return version; } // Export the Python hex version as a constant. diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index ba1b94c47984ab..8888f523559123 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -559,11 +559,7 @@ pycore_init_runtime(_PyRuntimeState *runtime, */ _PyRuntimeState_SetFinalizing(runtime, NULL); - status = _Py_GetVersion_Init(); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - + _Py_InitVersion(); _Py_DumpTraceback_Init(); status = _Py_HashRandomization_Init(config); diff --git a/Tools/c-analyzer/cpython/ignored.tsv b/Tools/c-analyzer/cpython/ignored.tsv index 5529a52b0c45c2..db9f74fdbc591f 100644 --- a/Tools/c-analyzer/cpython/ignored.tsv +++ b/Tools/c-analyzer/cpython/ignored.tsv @@ -48,8 +48,7 @@ Objects/unicodeobject.c _init_global_state initialized - Modules/getbuildinfo.c - buildinfo - Modules/getbuildinfo.c - initialized - Python/getversion.c - initialized - -Python/getversion.c - static_version - -Python/getversion.c - heap_version - +Python/getversion.c - version - ## public C-API - set during first init Python/bootstrap_hash.c - _Py_HashSecret_Initialized -