From 9bed8d35693b2ca2155284cc25d76fc39eb413ef Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 14 Sep 2026 14:46:08 +0200 Subject: [PATCH 1/2] gh-140550: Enable limited C API tests on Free Threading in test_cext Enable limited C API tests on Free Threading in test_cext and test_cppext: test Py_TARGET_ABI3T macro. * Convert test_cppext to PySlot API using PySlot_PTR_STATIC(). * Define Py_MOD_GIL_NOT_USED in test_cext and test_cppext. * Disable C++ test_virtual_object() if Py_TARGET_ABI3T is defined. * Remove ";" from PyABIInfo_VAR macro definition, since "PyABIInfo_VAR(abi_info);" added two ";;" which is illegal in C++03. --- Include/modsupport.h | 2 +- Lib/test/test_cext/__init__.py | 2 - Lib/test/test_cext/extension.c | 1 + Lib/test/test_cppext/__init__.py | 2 - Lib/test/test_cppext/extension.cpp | 64 +++++++++++-------- ...-09-14-14-50-42.gh-issue-140550.7jirEN.rst | 2 + 6 files changed, 40 insertions(+), 33 deletions(-) create mode 100644 Misc/NEWS.d/next/Tests/2026-09-14-14-50-42.gh-issue-140550.7jirEN.rst diff --git a/Include/modsupport.h b/Include/modsupport.h index cb47ad8cd2727fd..504a19d341a78a7 100644 --- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -140,7 +140,7 @@ PyAPI_FUNC(int) PyABIInfo_Check(PyABIInfo *info, const char *module_name); ///////////////////////////////////////////////////////// #define PyABIInfo_VAR(NAME) \ - static PyABIInfo NAME = _PyABIInfo_DEFAULT; + static PyABIInfo NAME = _PyABIInfo_DEFAULT #undef _PyABIInfo_DEFAULT_STABLE #undef _PyABIInfo_DEFAULT_FT diff --git a/Lib/test/test_cext/__init__.py b/Lib/test/test_cext/__init__.py index 4cc5f843dd388da..dfc7d230426cf8c 100644 --- a/Lib/test/test_cext/__init__.py +++ b/Lib/test/test_cext/__init__.py @@ -110,11 +110,9 @@ def run_cmd(operation, cmd): class TestPublicCAPI(BaseTests, unittest.TestCase): - @support.requires_gil_enabled('incompatible with Free Threading') def test_build_limited(self): self.check_build('_test_limited_cext', limited=True) - @support.requires_gil_enabled('broken for now with Free Threading') def test_build_limited_c11(self): self.check_build('_test_limited_c11_cext', limited=True, std='c11') diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c index 543a8096f16f8aa..b58c889ba8bcb3b 100644 --- a/Lib/test/test_cext/extension.c +++ b/Lib/test/test_cext/extension.c @@ -139,6 +139,7 @@ static PySlot _testcext_slots[] = { PySlot_STATIC_DATA(Py_mod_doc, (void*)(char*)_testcext_doc), PySlot_FUNC(Py_mod_exec, (void*)_testcext_exec), PySlot_STATIC_DATA(Py_mod_methods, _testcext_methods), + PySlot_DATA(Py_mod_gil, Py_MOD_GIL_NOT_USED), PySlot_END, }; diff --git a/Lib/test/test_cppext/__init__.py b/Lib/test/test_cppext/__init__.py index 967feee6693c037..db7f41d9ef7a11f 100644 --- a/Lib/test/test_cppext/__init__.py +++ b/Lib/test/test_cppext/__init__.py @@ -102,11 +102,9 @@ class TestPublicCAPI(BaseTests, unittest.TestCase): def test_build(self): self.check_build('_testcppext') - @support.requires_gil_enabled('incompatible with Free Threading') def test_build_limited_cpp03(self): self.check_build('_test_limited_cpp03ext', std='c++03', limited=True) - @support.requires_gil_enabled('incompatible with Free Threading') def test_build_limited(self): self.check_build('_testcppext_limited', limited=True) diff --git a/Lib/test/test_cppext/extension.cpp b/Lib/test/test_cppext/extension.cpp index 62ce81e2b510c7a..d5b50fbb48e18f5 100644 --- a/Lib/test/test_cppext/extension.cpp +++ b/Lib/test/test_cppext/extension.cpp @@ -1,4 +1,4 @@ -// gh-91321: Very basic C++ test extension to check that the Python C API is +// gh-91321: Basic C++ test extension to check that the Python C API is // compatible with C++ and does not emit C++ compiler warnings. // // The code is only built, not executed. @@ -159,6 +159,8 @@ test_unicode(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) Py_RETURN_NONE; } +// VirtualPyObject is incompatible with opaque PyObject +#ifndef Py_TARGET_ABI3T /* Test a `new`-allocated object with a virtual method. * (https://github.com/python/cpython/issues/94731) */ @@ -237,6 +239,8 @@ test_virtual_object(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) } Py_RETURN_NONE; } +#endif // Py_TARGET_ABI3T + static PyObject * test_datetime(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args)) @@ -256,7 +260,9 @@ static PyMethodDef _testcppext_methods[] = { {"add", _testcppext_add, METH_VARARGS, _testcppext_add_doc}, {"test_api_casts", test_api_casts, METH_NOARGS, _Py_NULL}, {"test_unicode", test_unicode, METH_NOARGS, _Py_NULL}, +#ifndef Py_TARGET_ABI3T {"test_virtual_object", test_virtual_object, METH_NOARGS, _Py_NULL}, +#endif {"test_datetime", test_datetime, METH_NOARGS, _Py_NULL}, // Note: _testcppext_exec currently runs all test functions directly. // When adding a new one, add a call there. @@ -282,9 +288,11 @@ _testcppext_exec(PyObject *module) if (!result) return -1; Py_DECREF(result); +#ifndef Py_TARGET_ABI3T result = PyObject_CallMethod(module, "test_virtual_object", ""); if (!result) return -1; Py_DECREF(result); +#endif result = PyObject_CallMethod(module, "test_datetime", ""); if (!result) return -1; @@ -313,40 +321,40 @@ _testcppext_exec(PyObject *module) return 0; } -// Need to ignore "-Wpedantic" warnings; see VirtualPyObject_Slots above -_Py_COMP_DIAG_PUSH -#if defined(__GNUC__) -#pragma GCC diagnostic ignored "-Wpedantic" -#elif defined(__clang__) -#pragma clang diagnostic ignored "-Wpedantic" -#endif -static PyModuleDef_Slot _testcppext_slots[] = { - {Py_mod_exec, reinterpret_cast(_testcppext_exec)}, - {0, _Py_NULL} +PyDoc_STRVAR(_testcppext_doc, "C++ test extension."); +PyABIInfo_VAR(abi_info); + +static PySlot _testcppext_slots[] = { + PySlot_PTR_STATIC(Py_mod_abi, &abi_info), + PySlot_PTR_STATIC(Py_mod_name, (void*)STR(MODULE_NAME)), + PySlot_PTR_STATIC(Py_mod_doc, (void*)(char*)_testcppext_doc), + PySlot_PTR_STATIC(Py_mod_exec, (void*)_testcppext_exec), + PySlot_PTR_STATIC(Py_mod_methods, _testcppext_methods), + PySlot_PTR_STATIC(Py_mod_gil, Py_MOD_GIL_NOT_USED), + PySlot_END, }; -_Py_COMP_DIAG_POP -PyDoc_STRVAR(_testcppext_doc, "C++ test extension."); +#define _FUNC_NAME(NAME) PyModExport_ ## NAME +#define FUNC_NAME(NAME) _FUNC_NAME(NAME) -static struct PyModuleDef _testcppext_module = { - PyModuleDef_HEAD_INIT, // m_base - STR(MODULE_NAME), // m_name - _testcppext_doc, // m_doc - 0, // m_size - _testcppext_methods, // m_methods - _testcppext_slots, // m_slots - _Py_NULL, // m_traverse - _Py_NULL, // m_clear - _Py_NULL, // m_free -}; +PyMODEXPORT_FUNC +FUNC_NAME(MODULE_NAME)(void) +{ + return _testcppext_slots; +} -#define _FUNC_NAME(NAME) PyInit_ ## NAME -#define FUNC_NAME(NAME) _FUNC_NAME(NAME) +// Also define the soft-deprecated entrypoint to ensure it isn't called + +#define _INITFUNC_NAME(NAME) PyInit_ ## NAME +#define INITFUNC_NAME(NAME) _INITFUNC_NAME(NAME) PyMODINIT_FUNC -FUNC_NAME(MODULE_NAME)(void) +INITFUNC_NAME(MODULE_NAME)(void) { - return PyModuleDef_Init(&_testcppext_module); + PyErr_SetString( + PyExc_AssertionError, + "PyInit_* function called while a PyModExport_* one is available"); + return NULL; } diff --git a/Misc/NEWS.d/next/Tests/2026-09-14-14-50-42.gh-issue-140550.7jirEN.rst b/Misc/NEWS.d/next/Tests/2026-09-14-14-50-42.gh-issue-140550.7jirEN.rst new file mode 100644 index 000000000000000..6de18208c1979c9 --- /dev/null +++ b/Misc/NEWS.d/next/Tests/2026-09-14-14-50-42.gh-issue-140550.7jirEN.rst @@ -0,0 +1,2 @@ +Enable limited C API tests on Free Threading in test_cext and test_cppext: +test the :c:macro:`Py_TARGET_ABI3T` macro. Patch by Victor Stinner. From 5b055821f649f963a78d772018151f03b38eaf53 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 14 Sep 2026 18:01:45 +0200 Subject: [PATCH 2/2] Ignore pedantic warnings on PySlot --- Lib/test/test_cppext/extension.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Lib/test/test_cppext/extension.cpp b/Lib/test/test_cppext/extension.cpp index d5b50fbb48e18f5..7496581c6756dac 100644 --- a/Lib/test/test_cppext/extension.cpp +++ b/Lib/test/test_cppext/extension.cpp @@ -325,6 +325,14 @@ _testcppext_exec(PyObject *module) PyDoc_STRVAR(_testcppext_doc, "C++ test extension."); PyABIInfo_VAR(abi_info); +// Need to ignore "-Wpedantic" warnings; see VirtualPyObject_Slots above +_Py_COMP_DIAG_PUSH +#if defined(__GNUC__) +#pragma GCC diagnostic ignored "-Wpedantic" +#elif defined(__clang__) +#pragma clang diagnostic ignored "-Wpedantic" +#endif + static PySlot _testcppext_slots[] = { PySlot_PTR_STATIC(Py_mod_abi, &abi_info), PySlot_PTR_STATIC(Py_mod_name, (void*)STR(MODULE_NAME)), @@ -335,6 +343,8 @@ static PySlot _testcppext_slots[] = { PySlot_END, }; +_Py_COMP_DIAG_POP + #define _FUNC_NAME(NAME) PyModExport_ ## NAME #define FUNC_NAME(NAME) _FUNC_NAME(NAME)