diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst index 771e0f2709a4aa..487c7559ef80ad 100644 --- a/Doc/library/sys.rst +++ b/Doc/library/sys.rst @@ -11,6 +11,24 @@ interpreter and to functions that interact strongly with the interpreter. It is always available. Unless explicitly noted otherwise, all variables are read-only. +.. data:: abi_info + + An object containing information about the ABI of the currently running + Python interpreter. The following attributes are available in cpython: + + *pointer_bits* is the width of pointers in bits, as an integer, equivalent + to ``8 * sizeof(void *)``, i.e. usually ``32`` or ``64``. + + *Py_GIL_DISABLED* is a boolean indicating whether the interpreter was built + with the GIL disabled, i.e. with the :option:`--disable-gil` option, + aka free-threading. + + *Py_DEBUG* is a boolean indicating whether the interpreter was built in + debug mode, i.e. with the :option:`--with-pydebug` option. + + .. versionadded:: next + + .. data:: abiflags On POSIX systems where Python was built with the standard ``configure`` diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index f89237931b7185..046f9eead9f74b 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -739,6 +739,15 @@ def test_thread_info(self): elif sys.platform == "wasi": self.assertEqual(info.name, "pthread-stubs") + def test_abi_info(self): + info = sys.abi_info + self.assertEqual(len(info.__dict__), 3) + pointer_bits = 64 if sys.maxsize > 2**32 else 32 + self.assertEqual(info.pointer_bits, pointer_bits) + for flag in ["Py_GIL_DISABLED", "Py_DEBUG"]: + self.assertEqual(getattr(info, flag, None), + bool(sysconfig.get_config_var(flag))) + @unittest.skipUnless(support.is_emscripten, "only available on Emscripten") def test_emscripten_info(self): self.assertEqual(len(sys._emscripten_info), 4) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-08-06-16-55-44.gh-issue-133143.l7CI9v.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-06-16-55-44.gh-issue-133143.l7CI9v.rst new file mode 100644 index 00000000000000..eaffb4022c6770 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-08-06-16-55-44.gh-issue-133143.l7CI9v.rst @@ -0,0 +1 @@ +Add ``sys.abi_info`` object to make ABI information more easily accessible. diff --git a/Python/sysmodule.c b/Python/sysmodule.c index ae6cf306735939..53296f3b4f9823 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -3260,6 +3260,7 @@ PyDoc_STR( "\n\ Static objects:\n\ \n\ +abi_info -- a named tuple with information about the ABI.\n\ builtin_module_names -- tuple of module names built into this interpreter\n\ copyright -- copyright notice pertaining to this interpreter\n\ exec_prefix -- prefix used to find the machine-specific Python library\n\ @@ -3630,6 +3631,56 @@ make_impl_info(PyObject *version_info) return NULL; } + +static PyObject * +make_abi_info(void) +{ + int res; + PyObject *abi_info, *value, *ns; + abi_info = PyDict_New(); + if (abi_info == NULL) { + goto error; + } + + value = PyLong_FromLong(sizeof(void *) * 8); + if (value == NULL) { + goto error; + } + res = PyDict_SetItemString(abi_info, "pointer_bits", value); + Py_DECREF(value); + if (res < 0) { + goto error; + } + +#ifdef Py_GIL_DISABLED + value = Py_True; +#else + value = Py_False; +#endif + res = PyDict_SetItemString(abi_info, "Py_GIL_DISABLED", value); + if (res < 0) { + goto error; + } + +#ifdef Py_DEBUG + value = Py_True; +#else + value = Py_False; +#endif + res = PyDict_SetItemString(abi_info, "Py_DEBUG", value); + if (res < 0) { + goto error; + } + + ns = _PyNamespace_New(abi_info); + Py_DECREF(abi_info); + return ns; + +error: + Py_CLEAR(abi_info); + return NULL; +} + #ifdef __EMSCRIPTEN__ PyDoc_STRVAR(emscripten_info__doc__, @@ -3855,6 +3906,8 @@ _PySys_InitCore(PyThreadState *tstate, PyObject *sysdict) SET_SYS("thread_info", PyThread_GetInfo()); + SET_SYS("abi_info", make_abi_info()); + /* initialize asyncgen_hooks */ if (_PyStructSequence_InitBuiltin(interp, &AsyncGenHooksType, &asyncgen_hooks_desc) < 0)