From 50669083fe16a42cba90b5dd8c1a017751f69fd8 Mon Sep 17 00:00:00 2001 From: "Gabriele N. Tornetta" Date: Fri, 10 Dec 2021 01:52:05 +0000 Subject: [PATCH] bpo-43931: Export Python version as API data (GH-25577) When Python is embedded in other applications, it is not easy to determine which version of Python is being used. This change exposes the Python version as part of the API data. Tools like Austin (https://github.com/P403n1x87/austin) can benefit from this data when targeting applications like uWSGI, as the Python version can then be inferred systematically by looking at the exported symbols rather than relying on unreliable pattern matching or other hacks (like remote code execution etc...). Automerge-Triggered-By: GH:pablogsal --- Doc/c-api/apiabiversion.rst | 9 +++++++++ Doc/c-api/init.rst | 2 ++ Doc/data/stable_abi.dat | 1 + Doc/whatsnew/3.11.rst | 4 ++++ Include/pylifecycle.h | 4 ++++ Lib/test/test_capi.py | 3 +++ Lib/test/test_stable_abi_ctypes.py | 1 + .../2021-04-24-15-39-23.bpo-43931.zpChDi.rst | 2 ++ Misc/stable_abi.txt | 3 +++ Modules/_testcapimodule.c | 1 + PC/python3dll.c | 1 + Python/getversion.c | 3 +++ 12 files changed, 34 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2021-04-24-15-39-23.bpo-43931.zpChDi.rst diff --git a/Doc/c-api/apiabiversion.rst b/Doc/c-api/apiabiversion.rst index 04050f7dabe..53a42e7f28e 100644 --- a/Doc/c-api/apiabiversion.rst +++ b/Doc/c-api/apiabiversion.rst @@ -58,5 +58,14 @@ See :ref:`stable` for a discussion of API and ABI stability across versions. Thus ``3.4.1a2`` is hexversion ``0x030401a2`` and ``3.10.0`` is hexversion ``0x030a00f0``. + This version is also available via the symbol :data:`Py_Version`. + +.. c:var:: const unsigned long Py_Version + + The Python runtime version number encoded in a single constant integer, with + the same format as the c:macro:`PY_VERSION_HEX` macro. + This contains the Python version used at run time. + + .. versionadded:: 3.11 All the given macros are defined in :source:`Include/patchlevel.h`. diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 09dfc68fee5..322b9e4d251 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -553,6 +553,8 @@ Process-wide parameters period. The returned string points into static storage; the caller should not modify its value. The value is available to Python code as :data:`sys.version`. + See also the :data:`Py_Version` constant. + .. c:function:: const char* Py_GetPlatform() diff --git a/Doc/data/stable_abi.dat b/Doc/data/stable_abi.dat index 64a0a2a247c..02e54e5d7f1 100644 --- a/Doc/data/stable_abi.dat +++ b/Doc/data/stable_abi.dat @@ -830,6 +830,7 @@ type,Py_UCS4,3.2, macro,Py_UNBLOCK_THREADS,3.2, var,Py_UTF8Mode,3.8, function,Py_VaBuildValue,3.2, +var,Py_Version,3.11, function,Py_XNewRef,3.10, type,Py_intptr_t,3.2, type,Py_ssize_t,3.2, diff --git a/Doc/whatsnew/3.11.rst b/Doc/whatsnew/3.11.rst index 7a828663e7f..8d1f4eba36e 100644 --- a/Doc/whatsnew/3.11.rst +++ b/Doc/whatsnew/3.11.rst @@ -626,6 +626,10 @@ New Features fields of the result from the exception instance (the ``value`` field). (Contributed by Irit Katriel in :issue:`45711`.) +* Added the :c:data:`Py_Version` constant which bears the same value as + :c:macro:`PY_VERSION_HEX`. + (Contributed by Gabriele N. Tornetta in :issue:`43931`.) + Porting to Python 3.11 ---------------------- diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index 4aecda235ab..e4c3b09c963 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -62,6 +62,10 @@ typedef void (*PyOS_sighandler_t)(int); PyAPI_FUNC(PyOS_sighandler_t) PyOS_getsig(int); PyAPI_FUNC(PyOS_sighandler_t) PyOS_setsig(int, PyOS_sighandler_t); +#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030B0000 +PyAPI_DATA(const unsigned long) Py_Version; +#endif + #ifndef Py_LIMITED_API # define Py_CPYTHON_PYLIFECYCLE_H # include "cpython/pylifecycle.h" diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index d51247003de..ecf3aa34ede 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -840,6 +840,9 @@ class Test_testcapi(unittest.TestCase): def test_widechar(self): _testcapi.test_widechar() + def test_version_api_data(self): + self.assertEqual(_testcapi.Py_Version, sys.hexversion) + class Test_testinternalcapi(unittest.TestCase): locals().update((name, getattr(_testinternalcapi, name)) diff --git a/Lib/test/test_stable_abi_ctypes.py b/Lib/test/test_stable_abi_ctypes.py index d0cd5c20dd5..9fd6b14b023 100644 --- a/Lib/test/test_stable_abi_ctypes.py +++ b/Lib/test/test_stable_abi_ctypes.py @@ -808,6 +808,7 @@ SYMBOL_NAMES = ( "Py_SetRecursionLimit", "Py_UTF8Mode", "Py_VaBuildValue", + "Py_Version", "Py_XNewRef", "_PyArg_ParseTupleAndKeywords_SizeT", "_PyArg_ParseTuple_SizeT", diff --git a/Misc/NEWS.d/next/Core and Builtins/2021-04-24-15-39-23.bpo-43931.zpChDi.rst b/Misc/NEWS.d/next/Core and Builtins/2021-04-24-15-39-23.bpo-43931.zpChDi.rst new file mode 100644 index 00000000000..03751291687 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2021-04-24-15-39-23.bpo-43931.zpChDi.rst @@ -0,0 +1,2 @@ +Added the :c:data:`Py_Version` constant which bears the same value as +:c:macro:`PY_VERSION_HEX`. Patch by Gabriele N. Tornetta. \ No newline at end of file diff --git a/Misc/stable_abi.txt b/Misc/stable_abi.txt index de6caa8c807..9cb210c60cf 100644 --- a/Misc/stable_abi.txt +++ b/Misc/stable_abi.txt @@ -2153,3 +2153,6 @@ data PyStructSequence_UnnamedField # (Detailed comments aren't really needed for further entries: from here on # we can use version control logs.) + +data Py_Version + added 3.11 diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 56d394985eb..6116365b2c0 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -7525,6 +7525,7 @@ PyInit__testcapi(void) PyModule_AddObject(m, "PY_SSIZE_T_MAX", PyLong_FromSsize_t(PY_SSIZE_T_MAX)); PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyLong_FromSsize_t(PY_SSIZE_T_MIN)); PyModule_AddObject(m, "SIZEOF_TIME_T", PyLong_FromSsize_t(sizeof(time_t))); + PyModule_AddObject(m, "Py_Version", PyLong_FromUnsignedLong(Py_Version)); Py_INCREF(&PyInstanceMethod_Type); PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type); diff --git a/PC/python3dll.c b/PC/python3dll.c index 6e469357ede..b2bb1706c4a 100755 --- a/PC/python3dll.c +++ b/PC/python3dll.c @@ -725,6 +725,7 @@ EXPORT_DATA(Py_FileSystemDefaultEncoding) EXPORT_DATA(Py_GenericAliasType) EXPORT_DATA(Py_HasFileSystemDefaultEncoding) EXPORT_DATA(Py_UTF8Mode) +EXPORT_DATA(Py_Version) EXPORT_DATA(PyBaseObject_Type) EXPORT_DATA(PyBool_Type) EXPORT_DATA(PyByteArray_Type) diff --git a/Python/getversion.c b/Python/getversion.c index c32b6f9d60d..46910451fdf 100644 --- a/Python/getversion.c +++ b/Python/getversion.c @@ -13,3 +13,6 @@ Py_GetVersion(void) PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler()); return version; } + +// Export the Python hex version as a constant. +const unsigned long Py_Version = PY_VERSION_HEX;