|
2 | 2 | /* Return the full version string. */ |
3 | 3 |
|
4 | 4 | #include "Python.h" |
| 5 | +#include "pycore_initconfig.h" // _PyStatus_NO_MEMORY() |
5 | 6 |
|
6 | | -#include "patchlevel.h" |
7 | 7 |
|
8 | 8 | static int initialized = 0; |
9 | | -static char version[300]; |
| 9 | +// Use the smallest possible buffer: it's only needed if malloc() fails *and* |
| 10 | +// Py_GetVersion() is called before Py_Initialize(). The buffer should be big |
| 11 | +// enough to store "3.16.0a0 " string. |
| 12 | +static char static_version[20]; |
| 13 | +static char *heap_version = NULL; |
10 | 14 |
|
11 | | -void _Py_InitVersion(void) |
| 15 | + |
| 16 | +PyStatus |
| 17 | +_Py_GetVersion_Init(void) |
12 | 18 | { |
13 | 19 | if (initialized) { |
14 | | - return; |
| 20 | + return _PyStatus_OK(); |
15 | 21 | } |
16 | | - initialized = 1; |
| 22 | + |
17 | 23 | #ifdef Py_GIL_DISABLED |
18 | | - const char *buildinfo_format = "%.80s free-threading build (%.80s) %.80s"; |
| 24 | + const char *format = "%s free-threading build (%s) %s"; |
| 25 | + size_t format_len = strlen(" free-threading build () "); |
19 | 26 | #else |
20 | | - const char *buildinfo_format = "%.80s (%.80s) %.80s"; |
| 27 | + const char *format = "%s (%s) %s"; |
| 28 | + size_t format_len = strlen(" () "); |
21 | 29 | #endif |
22 | | - PyOS_snprintf(version, sizeof(version), buildinfo_format, |
23 | | - PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler()); |
| 30 | + const char *version_str = PY_VERSION; |
| 31 | + const char *buildinfo = Py_GetBuildInfo(); |
| 32 | + const char *compiler = Py_GetCompiler(); |
| 33 | + // +1 for the trailing NUL byte |
| 34 | + size_t len = (format_len + strlen(version_str) + strlen(buildinfo) |
| 35 | + + strlen(compiler) + 1); |
| 36 | + |
| 37 | + // Always format the static version |
| 38 | + PyOS_snprintf(static_version, sizeof(static_version), format, |
| 39 | + version_str, buildinfo, compiler); |
| 40 | + |
| 41 | + heap_version = malloc(len); |
| 42 | + if (heap_version == NULL) { |
| 43 | + // If malloc() failed, don't set initialized to 1, so next |
| 44 | + // Py_GetVersion() will try again to allocate memory. |
| 45 | + return _PyStatus_NO_MEMORY(); |
| 46 | + } |
| 47 | + |
| 48 | + PyOS_snprintf(heap_version, len, format, |
| 49 | + version_str, buildinfo, compiler); |
| 50 | + initialized = 1; |
| 51 | + return _PyStatus_OK(); |
| 52 | +} |
| 53 | + |
| 54 | +void |
| 55 | +_Py_GetVersion_Fini(void) |
| 56 | +{ |
| 57 | + if (heap_version) { |
| 58 | + free(heap_version); |
| 59 | + heap_version = NULL; |
| 60 | + } |
24 | 61 | } |
25 | 62 |
|
26 | 63 | const char * |
27 | 64 | Py_GetVersion(void) |
28 | 65 | { |
29 | | - _Py_InitVersion(); |
30 | | - return version; |
| 66 | + PyStatus status = _Py_GetVersion_Init(); |
| 67 | + // Ignore error: Py_GetVersion() API cannot report error |
| 68 | + (void)status; |
| 69 | + |
| 70 | + if (heap_version) { |
| 71 | + return heap_version; |
| 72 | + } |
| 73 | + else { |
| 74 | + return static_version; |
| 75 | + } |
31 | 76 | } |
32 | 77 |
|
33 | 78 | // Export the Python hex version as a constant. |
|
0 commit comments