Skip to content

Commit 5066908

Browse files
authored
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
1 parent da3cf43 commit 5066908

File tree

12 files changed

+34
-0
lines changed

12 files changed

+34
-0
lines changed

Doc/c-api/apiabiversion.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,5 +58,14 @@ See :ref:`stable` for a discussion of API and ABI stability across versions.
5858
Thus ``3.4.1a2`` is hexversion ``0x030401a2`` and ``3.10.0`` is
5959
hexversion ``0x030a00f0``.
6060

61+
This version is also available via the symbol :data:`Py_Version`.
62+
63+
.. c:var:: const unsigned long Py_Version
64+
65+
The Python runtime version number encoded in a single constant integer, with
66+
the same format as the c:macro:`PY_VERSION_HEX` macro.
67+
This contains the Python version used at run time.
68+
69+
.. versionadded:: 3.11
6170

6271
All the given macros are defined in :source:`Include/patchlevel.h`.

Doc/c-api/init.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,8 @@ Process-wide parameters
553553
period. The returned string points into static storage; the caller should not
554554
modify its value. The value is available to Python code as :data:`sys.version`.
555555
556+
See also the :data:`Py_Version` constant.
557+
556558
557559
.. c:function:: const char* Py_GetPlatform()
558560

Doc/data/stable_abi.dat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -830,6 +830,7 @@ type,Py_UCS4,3.2,
830830
macro,Py_UNBLOCK_THREADS,3.2,
831831
var,Py_UTF8Mode,3.8,
832832
function,Py_VaBuildValue,3.2,
833+
var,Py_Version,3.11,
833834
function,Py_XNewRef,3.10,
834835
type,Py_intptr_t,3.2,
835836
type,Py_ssize_t,3.2,

Doc/whatsnew/3.11.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,10 @@ New Features
626626
fields of the result from the exception instance (the ``value`` field).
627627
(Contributed by Irit Katriel in :issue:`45711`.)
628628

629+
* Added the :c:data:`Py_Version` constant which bears the same value as
630+
:c:macro:`PY_VERSION_HEX`.
631+
(Contributed by Gabriele N. Tornetta in :issue:`43931`.)
632+
629633

630634
Porting to Python 3.11
631635
----------------------

Include/pylifecycle.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ typedef void (*PyOS_sighandler_t)(int);
6262
PyAPI_FUNC(PyOS_sighandler_t) PyOS_getsig(int);
6363
PyAPI_FUNC(PyOS_sighandler_t) PyOS_setsig(int, PyOS_sighandler_t);
6464

65+
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030B0000
66+
PyAPI_DATA(const unsigned long) Py_Version;
67+
#endif
68+
6569
#ifndef Py_LIMITED_API
6670
# define Py_CPYTHON_PYLIFECYCLE_H
6771
# include "cpython/pylifecycle.h"

Lib/test/test_capi.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,9 @@ class Test_testcapi(unittest.TestCase):
840840
def test_widechar(self):
841841
_testcapi.test_widechar()
842842

843+
def test_version_api_data(self):
844+
self.assertEqual(_testcapi.Py_Version, sys.hexversion)
845+
843846

844847
class Test_testinternalcapi(unittest.TestCase):
845848
locals().update((name, getattr(_testinternalcapi, name))

Lib/test/test_stable_abi_ctypes.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -808,6 +808,7 @@ def test_available_symbols(self):
808808
"Py_SetRecursionLimit",
809809
"Py_UTF8Mode",
810810
"Py_VaBuildValue",
811+
"Py_Version",
811812
"Py_XNewRef",
812813
"_PyArg_ParseTupleAndKeywords_SizeT",
813814
"_PyArg_ParseTuple_SizeT",
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Added the :c:data:`Py_Version` constant which bears the same value as
2+
:c:macro:`PY_VERSION_HEX`. Patch by Gabriele N. Tornetta.

Misc/stable_abi.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2153,3 +2153,6 @@ data PyStructSequence_UnnamedField
21532153

21542154
# (Detailed comments aren't really needed for further entries: from here on
21552155
# we can use version control logs.)
2156+
2157+
data Py_Version
2158+
added 3.11

Modules/_testcapimodule.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7525,6 +7525,7 @@ PyInit__testcapi(void)
75257525
PyModule_AddObject(m, "PY_SSIZE_T_MAX", PyLong_FromSsize_t(PY_SSIZE_T_MAX));
75267526
PyModule_AddObject(m, "PY_SSIZE_T_MIN", PyLong_FromSsize_t(PY_SSIZE_T_MIN));
75277527
PyModule_AddObject(m, "SIZEOF_TIME_T", PyLong_FromSsize_t(sizeof(time_t)));
7528+
PyModule_AddObject(m, "Py_Version", PyLong_FromUnsignedLong(Py_Version));
75287529
Py_INCREF(&PyInstanceMethod_Type);
75297530
PyModule_AddObject(m, "instancemethod", (PyObject *)&PyInstanceMethod_Type);
75307531

PC/python3dll.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ EXPORT_DATA(Py_FileSystemDefaultEncoding)
725725
EXPORT_DATA(Py_GenericAliasType)
726726
EXPORT_DATA(Py_HasFileSystemDefaultEncoding)
727727
EXPORT_DATA(Py_UTF8Mode)
728+
EXPORT_DATA(Py_Version)
728729
EXPORT_DATA(PyBaseObject_Type)
729730
EXPORT_DATA(PyBool_Type)
730731
EXPORT_DATA(PyByteArray_Type)

Python/getversion.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ Py_GetVersion(void)
1313
PY_VERSION, Py_GetBuildInfo(), Py_GetCompiler());
1414
return version;
1515
}
16+
17+
// Export the Python hex version as a constant.
18+
const unsigned long Py_Version = PY_VERSION_HEX;

0 commit comments

Comments
 (0)