Skip to content

Commit

Permalink
pythongh-95991: Add some infrastructure for testing Limited API in _t…
Browse files Browse the repository at this point in the history
…estcapi

- 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.)
  • Loading branch information
encukou committed Aug 15, 2022
1 parent 4a7f5a5 commit 368e434
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 21 deletions.
10 changes: 10 additions & 0 deletions Lib/test/support/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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.skipIf(True, 'needs _testcapi module')(test)
return unittest.skipIf(
not _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 = []
Expand Down
6 changes: 2 additions & 4 deletions Lib/test/test_call.py
Original file line number Diff line number Diff line change
@@ -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:
Expand Down Expand Up @@ -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()
Expand Down
32 changes: 31 additions & 1 deletion Modules/_testcapi/parts.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,39 @@
#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
#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"

/* Always enable assertions */
#undef NDEBUG

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
16 changes: 3 additions & 13 deletions Modules/_testcapi/vectorcall_limited.c
Original file line number Diff line number Diff line change
@@ -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 */
Expand Down Expand Up @@ -89,4 +79,4 @@ _PyTestCapi_Init_VectorcallLimited(PyObject *m) {
return 0;
}

#endif // Py_TRACE_REFS
#endif // LIMITED_API_AVAILABLE
12 changes: 9 additions & 3 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -6544,16 +6544,22 @@ 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;
}
if (_PyTestCapi_Init_Unicode(m) < 0) {
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;
}
Expand Down

0 comments on commit 368e434

Please sign in to comment.