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