Skip to content

Commit

Permalink
gh-109723: Disable Py_BUILD_CORE in _testcapi (#109727)
Browse files Browse the repository at this point in the history
Make sure that the internal C API is not tested by mistake by
_testcapi.

Undefine Py_BUILD_CORE_BUILTIN and Py_BUILD_CORE_MODULE macros in
Modules/_testcapi/parts.h: move code from _testcapimodule.c.

heaptype_relative.c and vectorcall_limited.c are using the limited C
API which is incompatible with the internal C API.

Move test_long_numbits() from _testcapi to _testinternalcapi since it
uses the internal C API "pycore_long.h".

Fix Modules/_testcapi/pyatomic.c: don't include Python.h directly,
just include _testcapi/parts.h.

Ajust "make check-c-globals" for these changes.
  • Loading branch information
vstinner authored Sep 22, 2023
1 parent c32abf1 commit 09a2561
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 101 deletions.
19 changes: 1 addition & 18 deletions Modules/_testcapi/clinic/long.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

53 changes: 0 additions & 53 deletions Modules/_testcapi/long.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

#include "parts.h"
#include "clinic/long.c.h"
#include "pycore_long.h" // _PyLong_Sign()

/*[clinic input]
module _testcapi
Expand Down Expand Up @@ -535,57 +534,6 @@ _testcapi_test_long_as_double_impl(PyObject *module)
return Py_None;
}

/*[clinic input]
_testcapi.test_long_numbits
[clinic start generated code]*/

static PyObject *
_testcapi_test_long_numbits_impl(PyObject *module)
/*[clinic end generated code: output=9eaf8458cb15d7f7 input=265c02d48a13059e]*/
{
struct triple {
long input;
size_t nbits;
int sign;
} testcases[] = {{0, 0, 0},
{1L, 1, 1},
{-1L, 1, -1},
{2L, 2, 1},
{-2L, 2, -1},
{3L, 2, 1},
{-3L, 2, -1},
{4L, 3, 1},
{-4L, 3, -1},
{0x7fffL, 15, 1}, /* one Python int digit */
{-0x7fffL, 15, -1},
{0xffffL, 16, 1},
{-0xffffL, 16, -1},
{0xfffffffL, 28, 1},
{-0xfffffffL, 28, -1}};
size_t i;

for (i = 0; i < Py_ARRAY_LENGTH(testcases); ++i) {
size_t nbits;
int sign;
PyObject *plong;

plong = PyLong_FromLong(testcases[i].input);
if (plong == NULL)
return NULL;
nbits = _PyLong_NumBits(plong);
sign = _PyLong_Sign(plong);

Py_DECREF(plong);
if (nbits != testcases[i].nbits)
return raiseTestError("test_long_numbits",
"wrong result for _PyLong_NumBits");
if (sign != testcases[i].sign)
return raiseTestError("test_long_numbits",
"wrong result for _PyLong_Sign");
}
Py_RETURN_NONE;
}

/*[clinic input]
_testcapi.call_long_compact_api
arg: object
Expand Down Expand Up @@ -631,7 +579,6 @@ static PyMethodDef test_methods[] = {
_TESTCAPI_TEST_LONG_AS_SIZE_T_METHODDEF
_TESTCAPI_TEST_LONG_AS_UNSIGNED_LONG_LONG_MASK_METHODDEF
_TESTCAPI_TEST_LONG_LONG_AND_OVERFLOW_METHODDEF
_TESTCAPI_TEST_LONG_NUMBITS_METHODDEF
_TESTCAPI_TEST_LONGLONG_API_METHODDEF
_TESTCAPI_CALL_LONG_COMPACT_API_METHODDEF
_TESTCAPI_PYLONG_ASINT_METHODDEF
Expand Down
16 changes: 16 additions & 0 deletions Modules/_testcapi/parts.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,24 @@
// Always enable assertions
#undef NDEBUG

// The _testcapi extension tests the public C API: header files in Include/ and
// Include/cpython/ directories. The internal C API must not be tested by
// _testcapi: use _testinternalcapi for that.
//
// _testcapi C files can built with the Py_BUILD_CORE_BUILTIN macro defined if
// one of the Modules/Setup files asks to build _testcapi as "static"
// (gh-109723).
//
// The Visual Studio projects builds _testcapi with Py_BUILD_CORE_MODULE.
#undef Py_BUILD_CORE_MODULE
#undef Py_BUILD_CORE_BUILTIN

#include "Python.h"

#ifdef Py_BUILD_CORE
# error "_testcapi must test the public Python C API, not the internal C API"
#endif

int _PyTestCapi_Init_Vectorcall(PyObject *module);
int _PyTestCapi_Init_Heaptype(PyObject *module);
int _PyTestCapi_Init_Abstract(PyObject *module);
Expand Down
4 changes: 0 additions & 4 deletions Modules/_testcapi/pyatomic.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@
* This only tests basic functionality, not any synchronizing ordering.
*/

/* Always enable assertions */
#undef NDEBUG

#include "Python.h"
#include "parts.h"

// We define atomic bitwise operations on these types
Expand Down
25 changes: 6 additions & 19 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,13 @@
* standard Python regression test, via Lib/test/test_capi.py.
*/

/* This module tests the public (Include/ and Include/cpython/) C API.
The internal C API must not be used here: use _testinternalcapi for that.
The Visual Studio projects builds _testcapi with Py_BUILD_CORE_MODULE
macro defined, but only the public C API must be tested here. */

#undef Py_BUILD_CORE_MODULE
#undef Py_BUILD_CORE_BUILTIN

/* Always enable assertions */
#undef NDEBUG
// Include parts.h first since it takes care of NDEBUG and Py_BUILD_CORE macros
// and including Python.h.
//
// Several parts of this module are broken out into files in _testcapi/.
// Include definitions from there.
#include "_testcapi/parts.h"

#include "Python.h"
#include "frameobject.h" // PyFrame_New()
#include "marshal.h" // PyMarshal_WriteLongToFile()

Expand All @@ -29,17 +23,10 @@
# include <sys/wait.h> // W_STOPCODE
#endif

#ifdef Py_BUILD_CORE
# error "_testcapi must test the public Python C API, not CPython internal C API"
#endif

#ifdef bool
# error "The public headers should not include <stdbool.h>, see gh-48924"
#endif

// Several parts of this module are broken out into files in _testcapi/.
// Include definitions from there.
#include "_testcapi/parts.h"
#include "_testcapi/util.h"


Expand Down
62 changes: 62 additions & 0 deletions Modules/_testinternalcapi.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "pycore_hashtable.h" // _Py_hashtable_new()
#include "pycore_initconfig.h" // _Py_GetConfigsAsDict()
#include "pycore_interp.h" // _PyInterpreterState_GetConfigCopy()
#include "pycore_long.h" // _PyLong_Sign()
#include "pycore_object.h" // _PyObject_IsFreed()
#include "pycore_pathconfig.h" // _PyPathConfig_ClearGlobal()
#include "pycore_pyerrors.h" // _PyErr_ChainExceptions1()
Expand Down Expand Up @@ -1466,6 +1467,66 @@ _testinternalcapi_write_unraisable_exc_impl(PyObject *module, PyObject *exc,
}


static PyObject *
raiseTestError(const char* test_name, const char* msg)
{
PyErr_Format(PyExc_AssertionError, "%s: %s", test_name, msg);
return NULL;
}


/*[clinic input]
_testinternalcapi.test_long_numbits
[clinic start generated code]*/

static PyObject *
_testinternalcapi_test_long_numbits_impl(PyObject *module)
/*[clinic end generated code: output=745d62d120359434 input=f14ca6f638e44dad]*/
{
struct triple {
long input;
size_t nbits;
int sign;
} testcases[] = {{0, 0, 0},
{1L, 1, 1},
{-1L, 1, -1},
{2L, 2, 1},
{-2L, 2, -1},
{3L, 2, 1},
{-3L, 2, -1},
{4L, 3, 1},
{-4L, 3, -1},
{0x7fffL, 15, 1}, /* one Python int digit */
{-0x7fffL, 15, -1},
{0xffffL, 16, 1},
{-0xffffL, 16, -1},
{0xfffffffL, 28, 1},
{-0xfffffffL, 28, -1}};
size_t i;

for (i = 0; i < Py_ARRAY_LENGTH(testcases); ++i) {
size_t nbits;
int sign;
PyObject *plong;

plong = PyLong_FromLong(testcases[i].input);
if (plong == NULL)
return NULL;
nbits = _PyLong_NumBits(plong);
sign = _PyLong_Sign(plong);

Py_DECREF(plong);
if (nbits != testcases[i].nbits)
return raiseTestError("test_long_numbits",
"wrong result for _PyLong_NumBits");
if (sign != testcases[i].sign)
return raiseTestError("test_long_numbits",
"wrong result for _PyLong_Sign");
}
Py_RETURN_NONE;
}


static PyMethodDef module_functions[] = {
{"get_configs", get_configs, METH_NOARGS},
{"get_recursion_depth", get_recursion_depth, METH_NOARGS},
Expand Down Expand Up @@ -1521,6 +1582,7 @@ static PyMethodDef module_functions[] = {
_PyCFunction_CAST(run_in_subinterp_with_config),
METH_VARARGS | METH_KEYWORDS},
_TESTINTERNALCAPI_WRITE_UNRAISABLE_EXC_METHODDEF
_TESTINTERNALCAPI_TEST_LONG_NUMBITS_METHODDEF
{NULL, NULL} /* sentinel */
};

Expand Down
19 changes: 18 additions & 1 deletion Modules/clinic/_testinternalcapi.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions Tools/c-analyzer/c_parser/preprocessor/gcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@

from . import common as _common

# The following C files define the Py_LIMITED_API macro, and so must not be
# built with the Py_BUILD_CORE macro defined.
USE_LIMITED_C_API = frozenset((
# The following C files must not built with Py_BUILD_CORE.
FILES_WITHOUT_INTERNAL_CAPI = frozenset((
# Modules/
'_testcapimodule.c',
'_testclinic_limited.c',
'xxlimited.c',
'xxlimited_35.c',
))

# C files in the fhe following directories must not be built with
# Py_BUILD_CORE.
DIRS_WITHOUT_INTERNAL_CAPI = frozenset((
# Modules/_testcapi/
'heaptype_relative.c',
'vectorcall_limited.c',
'_testcapi',
))

TOOL = 'gcc'
Expand Down Expand Up @@ -75,7 +77,10 @@ def preprocess(filename,
filename = _normpath(filename, cwd)

postargs = POST_ARGS
if os.path.basename(filename) not in USE_LIMITED_C_API:
basename = os.path.basename(filename)
dirname = os.path.basename(os.path.dirname(filename))
if (basename not in FILES_WITHOUT_INTERNAL_CAPI
and dirname not in DIRS_WITHOUT_INTERNAL_CAPI):
postargs += ('-DPy_BUILD_CORE=1',)

text = _common.preprocess(
Expand Down

0 comments on commit 09a2561

Please sign in to comment.