diff --git a/Doc/library/test.rst b/Doc/library/test.rst index e255952d457..f3bc7e7560a 100644 --- a/Doc/library/test.rst +++ b/Doc/library/test.rst @@ -794,6 +794,12 @@ The :mod:`test.support` module defines the following functions: Decorator for only running the test if :data:`HAVE_DOCSTRINGS`. +.. decorator:: requires_limited_api + + Decorator for only running the test if :ref:`Limited C API ` + is available. + + .. decorator:: cpython_only Decorator for tests only applicable to CPython. diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py index c51a1f26f29..2409fb05d72 100644 --- a/Lib/test/support/__init__.py +++ b/Lib/test/support/__init__.py @@ -46,6 +46,7 @@ __all__ = [ "anticipate_failure", "load_package_tests", "detect_api_mismatch", "check__all__", "skip_if_buggy_ucrt_strfptime", "check_disallow_instantiation", "check_sanitizer", "skip_if_sanitizer", + "requires_limited_api", # sys "is_jython", "is_android", "is_emscripten", "is_wasi", "check_impl_detail", "unix_shell", "setswitchinterval", @@ -1069,6 +1070,15 @@ def refcount_test(test): return no_tracing(cpython_only(test)) +def requires_limited_api(test): + try: + import _testcapi + except ImportError: + return unittest.skip('needs _testcapi module')(test) + return unittest.skipUnless( + _testcapi.LIMITED_API_AVAILABLE, 'needs Limited API support')(test) + + def _filter_suite(suite, pred): """Recursively filter test cases in a suite based on a predicate.""" newtests = [] diff --git a/Lib/test/test_call.py b/Lib/test/test_call.py index 131b45e6caa..c00de27b265 100644 --- a/Lib/test/test_call.py +++ b/Lib/test/test_call.py @@ -1,5 +1,5 @@ import unittest -from test.support import cpython_only +from test.support import cpython_only, requires_limited_api try: import _testcapi except ImportError: @@ -760,9 +760,7 @@ class TestPEP590(unittest.TestCase): self.assertEqual(expected, meth(*args1, **kwargs)) self.assertEqual(expected, wrapped(*args, **kwargs)) - @unittest.skipIf( - hasattr(sys, 'getobjects'), - "Limited API is not compatible with Py_TRACE_REFS") + @requires_limited_api def test_vectorcall_limited(self): from _testcapi import pyobject_vectorcall obj = _testcapi.LimitedVectorCallClass() diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h index a76ddd93c0e..304e5922c0d 100644 --- a/Modules/_testcapi/parts.h +++ b/Modules/_testcapi/parts.h @@ -1,9 +1,36 @@ -#include "Python.h" +#ifndef Py_TESTCAPI_PARTS_H +#define Py_TESTCAPI_PARTS_H -/* Always enable assertions */ +#include "pyconfig.h" // for Py_TRACE_REFS + +// Figure out if Limited API is available for this build. If it isn't we won't +// build tests for it. +// Currently, only Py_TRACE_REFS disables Limited API. +#ifdef Py_TRACE_REFS +#undef LIMITED_API_AVAILABLE +#else +#define LIMITED_API_AVAILABLE 1 +#endif + +// Always enable assertions #undef NDEBUG +#if !defined(LIMITED_API_AVAILABLE) && defined(Py_LIMITED_API) +// Limited API being unavailable means that with Py_LIMITED_API defined +// we can't even include Python.h. +// Do nothing; the .c file that defined Py_LIMITED_API should also do nothing. + +#else + +#include "Python.h" + int _PyTestCapi_Init_Vectorcall(PyObject *module); -int _PyTestCapi_Init_VectorcallLimited(PyObject *module); int _PyTestCapi_Init_Heaptype(PyObject *module); int _PyTestCapi_Init_Unicode(PyObject *module); + +#ifdef LIMITED_API_AVAILABLE +int _PyTestCapi_Init_VectorcallLimited(PyObject *module); +#endif // LIMITED_API_AVAILABLE + +#endif +#endif // Py_TESTCAPI_PARTS_H diff --git a/Modules/_testcapi/vectorcall_limited.c b/Modules/_testcapi/vectorcall_limited.c index c5184318e29..ee57af84b1b 100644 --- a/Modules/_testcapi/vectorcall_limited.c +++ b/Modules/_testcapi/vectorcall_limited.c @@ -1,18 +1,8 @@ -#include "pyconfig.h" // Py_TRACE_REFS - -#ifdef Py_TRACE_REFS - -// Py_TRACE_REFS is incompatible with Limited API -#include "parts.h" -int -_PyTestCapi_Init_VectorcallLimited(PyObject *m) { - return 0; -} - -#else - #define Py_LIMITED_API 0x030c0000 // 3.12 #include "parts.h" + +#ifdef LIMITED_API_AVAILABLE + #include "structmember.h" // PyMemberDef /* Test Vectorcall in the limited API */ @@ -89,4 +79,4 @@ _PyTestCapi_Init_VectorcallLimited(PyObject *m) { return 0; } -#endif // Py_TRACE_REFS +#endif // LIMITED_API_AVAILABLE diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 8d9a0c15b1b..2d4c73cfe97 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -6544,9 +6544,6 @@ PyInit__testcapi(void) if (_PyTestCapi_Init_Vectorcall(m) < 0) { return NULL; } - if (_PyTestCapi_Init_VectorcallLimited(m) < 0) { - return NULL; - } if (_PyTestCapi_Init_Heaptype(m) < 0) { return NULL; } @@ -6554,6 +6551,15 @@ PyInit__testcapi(void) return NULL; } +#ifndef LIMITED_API_AVAILABLE + PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_False); +#else + PyModule_AddObjectRef(m, "LIMITED_API_AVAILABLE", Py_True); + if (_PyTestCapi_Init_VectorcallLimited(m) < 0) { + return NULL; + } +#endif + PyState_AddModule(m, &_testcapimodule); return m; } diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj index 23bb5ec8527..b7d40c8cdc3 100644 --- a/PCbuild/_testcapi.vcxproj +++ b/PCbuild/_testcapi.vcxproj @@ -107,6 +107,10 @@ {cf7ac3d1-e2df-41d2-bea6-1e2556cdea26} false + + {885d4898-d08d-4091-9c40-c700cfe3fc5a} + false +