diff --git a/docs/api.rst b/docs/api.rst index de44b8d..ec0ad0e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -10,6 +10,8 @@ Supported Python versions: * Python 3.6 - 3.16 * PyPy 3.6 - 3.12 +The limited C API is only supported on CPython. + A C11 subset (without optional features) is required, like ``static inline`` functions: see `PEP 7 `_. diff --git a/tests/extension.c b/tests/extension.c new file mode 100644 index 0000000..f0d3e5b --- /dev/null +++ b/tests/extension.c @@ -0,0 +1,77 @@ +// C extension made of two C files (extension.c and static_inline.c) to detect +// when a pythoncapi_compat.h function is implemented without "static inline". + +#ifndef MODULE_NAME +# error "MODULE_NAME macro is not set" +#endif + +#include "pythoncapi_compat.h" + +#define _STR(NAME) #NAME +#define STR(NAME) _STR(NAME) +#define _CONCAT(a, b) a ## b +#define CONCAT(a, b) _CONCAT(a, b) + + +// No method and no module exec method needed, most basic C extension + + +// Use PySlot API on Python 3.15 and newer and on limited C API 3.15 and newer +#if (PY_VERSION_HEX >= _Py_PACK_VERSION(3, 15) \ + && (!defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15))) + +PyABIInfo_VAR(abi_info); + +static PySlot module_slots[] = { + PySlot_PTR_STATIC(Py_mod_abi, &abi_info), + PySlot_PTR_STATIC(Py_mod_name, (void*)STR(MODULE_NAME)), + PySlot_PTR_STATIC(Py_mod_state_size, 0), + PySlot_PTR_STATIC(Py_mod_gil, Py_MOD_GIL_NOT_USED), + {0, 0, {0}, {0}} +}; + +#define EXPORT_FUNC CONCAT(PyModExport_, MODULE_NAME) + +PyMODEXPORT_FUNC +EXPORT_FUNC(void) +{ + return module_slots; +} + +#ifdef MS_WINDOWS +# define INIT_FUNC CONCAT(PyInit_, MODULE_NAME) + +// Workaround for https://github.com/pypa/distutils/issues/387 +// https://docs.python.org/3.16/howto/abi3t-migration.html#module-export-hook +PyMODINIT_FUNC +INIT_FUNC(void) +{ + PyErr_SetString(PyExc_SystemError, + "PyInit_* called for module with PyModExport_*"); + return NULL; +} +#endif + +#else + +static struct PyModuleDef module_def = { + PyModuleDef_HEAD_INIT, + STR(MODULE_NAME), // m_name + _Py_NULL, // m_doc + 0, // m_size + _Py_NULL, // m_methods + _Py_NULL, // m_slots + _Py_NULL, // m_traverse + _Py_NULL, // m_clear + _Py_NULL, // m_free +}; + + +#define INIT_FUNC CONCAT(PyInit_, MODULE_NAME) + +PyMODINIT_FUNC +INIT_FUNC(void) +{ + return PyModule_Create(&module_def); +} +#endif diff --git a/tests/setup.py b/tests/setup.py index ba79156..ff86cb4 100755 --- a/tests/setup.py +++ b/tests/setup.py @@ -202,6 +202,28 @@ def add_common_flags(flags, limited): language='c++') extensions.append(ext) + # Build a C extension with two C files to detect when a pythoncapi_compat.h + # function is implemented without "static inline". + sources = [ + 'extension.c', + 'static_inline.c', + ] + for limited in (False, True): + if limited and not TEST_LIMITED_C_API: + continue + name = 'test_pythoncapi_compat_ext' + assert name != C_EXTENSION_PREFIX + flags = list(COMMON_FLAGS) + if limited: + name += LIMITED_SUFFIX + flags.append(limited_flag) + flags.append(f'-DMODULE_NAME={name}') + ext = Extension( + name, + sources=sources, + extra_compile_args=flags) + extensions.append(ext) + setup(name="test_pythoncapi_compat", ext_modules=extensions) diff --git a/tests/static_inline.c b/tests/static_inline.c new file mode 100644 index 0000000..4e29f04 --- /dev/null +++ b/tests/static_inline.c @@ -0,0 +1,6 @@ +// C extension made of two C files (extension.c and static_inline.c) to detect +// when a pythoncapi_compat.h function is implemented without "static inline". + +#include "pythoncapi_compat.h" + +// No function defined, only #include "pythoncapi_compat.h" diff --git a/tests/test_pythoncapi_compat_cext.c b/tests/test_pythoncapi_compat_cext.c index d7383fe..1ef5946 100644 --- a/tests/test_pythoncapi_compat_cext.c +++ b/tests/test_pythoncapi_compat_cext.c @@ -1,6 +1,10 @@ // Always enable assertions #undef NDEBUG +#ifndef MODULE_NAME +# error "MODULE_NAME macro is not set" +#endif + #include "pythoncapi_compat.h" #include // T_SHORT, READONLY @@ -8,10 +12,6 @@ # error "assertions must be enabled" #endif -#ifndef MODULE_NAME -# error "MODULE_NAME macro is not set" -#endif - #define _STR(NAME) #NAME #define STR(NAME) _STR(NAME) #define _CONCAT(a, b) a ## b