cpython/Lib/test/test_cext/extension.c
Petr Viktorin c7d24b81c3
gh-111506: Add _Py_OPAQUE_PYOBJECT to hide PyObject layout & related API (GH-136505)
Allow Py_LIMITED_API for (Py_GIL_DISABLED && _Py_OPAQUE_PYOBJECT)


API that's removed when _Py_OPAQUE_PYOBJECT is defined:

    - PyObject_HEAD
    - _PyObject_EXTRA_INIT
    - PyObject_HEAD_INIT
    - PyObject_VAR_HEAD
    - struct _object (i.e. PyObject) (opaque)
    - struct PyVarObject (opaque)
    - Py_SIZE
    - Py_SET_TYPE
    - Py_SET_SIZE
    - PyModuleDef_Base (opaque)
    - PyModuleDef_HEAD_INIT
    - PyModuleDef (opaque)
    - _Py_IsImmortal
    - _Py_IsStaticImmortal

Note that the `_Py_IsImmortal` removal (and a few other issues)
 means _Py_OPAQUE_PYOBJECT only works with limited
API 3.14+ now.


Co-authored-by: Victor Stinner <vstinner@python.org>
2025-07-12 09:55:12 +02:00

145 lines
3.5 KiB
C

// gh-116869: Basic C test extension to check that the Python C API
// does not emit C compiler warnings.
//
// Test also the internal C API if the TEST_INTERNAL_C_API macro is defined.
// Always enable assertions
#undef NDEBUG
#ifdef TEST_INTERNAL_C_API
# define Py_BUILD_CORE_MODULE 1
#endif
#include "Python.h"
#ifdef TEST_INTERNAL_C_API
// gh-135906: Check for compiler warnings in the internal C API.
// - Cython uses pycore_frame.h.
// - greenlet uses pycore_frame.h, pycore_interpframe_structs.h and
// pycore_interpframe.h.
# include "internal/pycore_frame.h"
# include "internal/pycore_gc.h"
# include "internal/pycore_interp.h"
# include "internal/pycore_interpframe.h"
# include "internal/pycore_interpframe_structs.h"
# include "internal/pycore_object.h"
# include "internal/pycore_pystate.h"
#endif
#ifndef MODULE_NAME
# error "MODULE_NAME macro must be defined"
#endif
#define _STR(NAME) #NAME
#define STR(NAME) _STR(NAME)
PyDoc_STRVAR(_testcext_add_doc,
"add(x, y)\n"
"\n"
"Return the sum of two integers: x + y.");
static PyObject *
_testcext_add(PyObject *Py_UNUSED(module), PyObject *args)
{
long i, j, res;
if (!PyArg_ParseTuple(args, "ll:foo", &i, &j)) {
return NULL;
}
res = i + j;
return PyLong_FromLong(res);
}
static PyMethodDef _testcext_methods[] = {
{"add", _testcext_add, METH_VARARGS, _testcext_add_doc},
{NULL, NULL, 0, NULL} // sentinel
};
static int
_testcext_exec(
#ifdef __STDC_VERSION__
PyObject *module
#else
PyObject *Py_UNUSED(module)
#endif
)
{
#ifdef __STDC_VERSION__
if (PyModule_AddIntMacro(module, __STDC_VERSION__) < 0) {
return -1;
}
#endif
// test Py_BUILD_ASSERT() and Py_BUILD_ASSERT_EXPR()
Py_BUILD_ASSERT(sizeof(int) == sizeof(unsigned int));
assert(Py_BUILD_ASSERT_EXPR(sizeof(int) == sizeof(unsigned int)) == 0);
return 0;
}
#define _FUNC_NAME(NAME) PyInit_ ## NAME
#define FUNC_NAME(NAME) _FUNC_NAME(NAME)
// Converting from function pointer to void* has undefined behavior, but
// works on all known platforms, and CPython's module and type slots currently
// need it.
// (GCC doesn't have a narrower category for this than -Wpedantic.)
_Py_COMP_DIAG_PUSH
#if defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wpedantic"
#elif defined(__clang__)
#pragma clang diagnostic ignored "-Wpedantic"
#endif
static PyModuleDef_Slot _testcext_slots[] = {
{Py_mod_exec, (void*)_testcext_exec},
{0, NULL}
};
_Py_COMP_DIAG_POP
PyDoc_STRVAR(_testcext_doc, "C test extension.");
#ifndef _Py_OPAQUE_PYOBJECT
static struct PyModuleDef _testcext_module = {
PyModuleDef_HEAD_INIT, // m_base
STR(MODULE_NAME), // m_name
_testcext_doc, // m_doc
0, // m_size
_testcext_methods, // m_methods
_testcext_slots, // m_slots
NULL, // m_traverse
NULL, // m_clear
NULL, // m_free
};
PyMODINIT_FUNC
FUNC_NAME(MODULE_NAME)(void)
{
return PyModuleDef_Init(&_testcext_module);
}
#else // _Py_OPAQUE_PYOBJECT
// Opaque PyObject means that PyModuleDef is also opaque and cannot be
// declared statically. See PEP 793.
// So, this part of module creation is split into a separate source file
// which uses non-limited API.
// (repeated definition to avoid creating a header)
extern PyObject *testcext_create_moduledef(
const char *name, const char *doc,
PyMethodDef *methods, PyModuleDef_Slot *slots);
PyMODINIT_FUNC
FUNC_NAME(MODULE_NAME)(void)
{
return testcext_create_moduledef(
STR(MODULE_NAME), _testcext_doc, _testcext_methods, _testcext_slots);
}
#endif // _Py_OPAQUE_PYOBJECT