[Python-checkins] gh-95991: Add some infrastructure for testing Limited API in _testcapi (GH-95992)

encukou webhook-mailer at python.org
Wed Aug 17 07:49:04 EDT 2022


https://github.com/python/cpython/commit/0f2b469ce1a6f123ad9e151b1771651b3e1d2de6
commit: 0f2b469ce1a6f123ad9e151b1771651b3e1d2de6
branch: main
author: Petr Viktorin <encukou at gmail.com>
committer: encukou <encukou at gmail.com>
date: 2022-08-17T13:48:43+02:00
summary:

gh-95991: Add some infrastructure for testing Limited API in _testcapi (GH-95992)

- Limited API needs to be enabled per source file
- Some builds don't support Limited API, so Limited API tests must be skipped on those builds
  (currently this is `Py_TRACE_REFS`, but that may change.)
- `Py_LIMITED_API` must be defined before `<Python.h>` is included.

This puts the hoop-jumping in `testcapi/parts.h`, so individual
test files can be relatively simple. (Currently that's only
`vectorcall_limited.c`, imagine more.)

files:
M Doc/library/test.rst
M Lib/test/support/__init__.py
M Lib/test/test_call.py
M Modules/_testcapi/parts.h
M Modules/_testcapi/vectorcall_limited.c
M Modules/_testcapimodule.c
M PCbuild/_testcapi.vcxproj

diff --git a/Doc/library/test.rst b/Doc/library/test.rst
index e255952d4570..f3bc7e7560a6 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 <stable>`
+   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 c51a1f26f29a..2409fb05d728 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -46,6 +46,7 @@
     "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 131b45e6caaa..c00de27b265d 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 @@ def __call__(self, *args):
                 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 a76ddd93c0ef..304e5922c0d5 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
+
+#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 */
+// 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 c5184318e292..ee57af84b1bb 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
+#define Py_LIMITED_API 0x030c0000 // 3.12
 #include "parts.h"
-int
-_PyTestCapi_Init_VectorcallLimited(PyObject *m) {
-    return 0;
-}
 
-#else
+#ifdef LIMITED_API_AVAILABLE
 
-#define Py_LIMITED_API 0x030c0000 // 3.12
-#include "parts.h"
 #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 8d9a0c15b1b0..2d4c73cfe970 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 23bb5ec85274..b7d40c8cdc30 100644
--- a/PCbuild/_testcapi.vcxproj
+++ b/PCbuild/_testcapi.vcxproj
@@ -107,6 +107,10 @@
       <Project>{cf7ac3d1-e2df-41d2-bea6-1e2556cdea26}</Project>
       <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
     </ProjectReference>
+    <ProjectReference Include="python3dll.vcxproj">
+      <Project>{885d4898-d08d-4091-9c40-c700cfe3fc5a}</Project>
+      <ReferenceOutputAssembly>false</ReferenceOutputAssembly>
+    </ProjectReference>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">



More information about the Python-checkins mailing list