Skip to content

Commit b8fe76f

Browse files
authored
Check for 'static inline' (#206)
Build a C extension with two C files to check that all pythoncapi_compat.h functions are declared with "static inline".
1 parent b7e486f commit b8fe76f

5 files changed

Lines changed: 111 additions & 4 deletions

File tree

‎docs/api.rst‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Supported Python versions:
1010
* Python 3.6 - 3.16
1111
* PyPy 3.6 - 3.12
1212

13+
The limited C API is only supported on CPython.
14+
1315
A C11 subset (without optional features) is required, like ``static inline``
1416
functions: see `PEP 7 <https://www.python.org/dev/peps/pep-0007/>`_.
1517

‎tests/extension.c‎

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// C extension made of two C files (extension.c and static_inline.c) to detect
2+
// when a pythoncapi_compat.h function is implemented without "static inline".
3+
4+
#ifndef MODULE_NAME
5+
# error "MODULE_NAME macro is not set"
6+
#endif
7+
8+
#include "pythoncapi_compat.h"
9+
10+
#define _STR(NAME) #NAME
11+
#define STR(NAME) _STR(NAME)
12+
#define _CONCAT(a, b) a ## b
13+
#define CONCAT(a, b) _CONCAT(a, b)
14+
15+
16+
// No method and no module exec method needed, most basic C extension
17+
18+
19+
// Use PySlot API on Python 3.15 and newer and on limited C API 3.15 and newer
20+
#if (PY_VERSION_HEX >= _Py_PACK_VERSION(3, 15) \
21+
&& (!defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= _Py_PACK_VERSION(3, 15)))
22+
23+
PyABIInfo_VAR(abi_info);
24+
25+
static PySlot module_slots[] = {
26+
PySlot_PTR_STATIC(Py_mod_abi, &abi_info),
27+
PySlot_PTR_STATIC(Py_mod_name, (void*)STR(MODULE_NAME)),
28+
PySlot_PTR_STATIC(Py_mod_state_size, 0),
29+
PySlot_PTR_STATIC(Py_mod_gil, Py_MOD_GIL_NOT_USED),
30+
{0, 0, {0}, {0}}
31+
};
32+
33+
#define EXPORT_FUNC CONCAT(PyModExport_, MODULE_NAME)
34+
35+
PyMODEXPORT_FUNC
36+
EXPORT_FUNC(void)
37+
{
38+
return module_slots;
39+
}
40+
41+
#ifdef MS_WINDOWS
42+
# define INIT_FUNC CONCAT(PyInit_, MODULE_NAME)
43+
44+
// Workaround for https://github.com/pypa/distutils/issues/387
45+
// https://docs.python.org/3.16/howto/abi3t-migration.html#module-export-hook
46+
PyMODINIT_FUNC
47+
INIT_FUNC(void)
48+
{
49+
PyErr_SetString(PyExc_SystemError,
50+
"PyInit_* called for module with PyModExport_*");
51+
return NULL;
52+
}
53+
#endif
54+
55+
#else
56+
57+
static struct PyModuleDef module_def = {
58+
PyModuleDef_HEAD_INIT,
59+
STR(MODULE_NAME), // m_name
60+
_Py_NULL, // m_doc
61+
0, // m_size
62+
_Py_NULL, // m_methods
63+
_Py_NULL, // m_slots
64+
_Py_NULL, // m_traverse
65+
_Py_NULL, // m_clear
66+
_Py_NULL, // m_free
67+
};
68+
69+
70+
#define INIT_FUNC CONCAT(PyInit_, MODULE_NAME)
71+
72+
PyMODINIT_FUNC
73+
INIT_FUNC(void)
74+
{
75+
return PyModule_Create(&module_def);
76+
}
77+
#endif

‎tests/setup.py‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,28 @@ def add_common_flags(flags, limited):
202202
language='c++')
203203
extensions.append(ext)
204204

205+
# Build a C extension with two C files to detect when a pythoncapi_compat.h
206+
# function is implemented without "static inline".
207+
sources = [
208+
'extension.c',
209+
'static_inline.c',
210+
]
211+
for limited in (False, True):
212+
if limited and not TEST_LIMITED_C_API:
213+
continue
214+
name = 'test_pythoncapi_compat_ext'
215+
assert name != C_EXTENSION_PREFIX
216+
flags = list(COMMON_FLAGS)
217+
if limited:
218+
name += LIMITED_SUFFIX
219+
flags.append(limited_flag)
220+
flags.append(f'-DMODULE_NAME={name}')
221+
ext = Extension(
222+
name,
223+
sources=sources,
224+
extra_compile_args=flags)
225+
extensions.append(ext)
226+
205227
setup(name="test_pythoncapi_compat", ext_modules=extensions)
206228

207229

‎tests/static_inline.c‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// C extension made of two C files (extension.c and static_inline.c) to detect
2+
// when a pythoncapi_compat.h function is implemented without "static inline".
3+
4+
#include "pythoncapi_compat.h"
5+
6+
// No function defined, only #include "pythoncapi_compat.h"

‎tests/test_pythoncapi_compat_cext.c‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
// Always enable assertions
22
#undef NDEBUG
33

4+
#ifndef MODULE_NAME
5+
# error "MODULE_NAME macro is not set"
6+
#endif
7+
48
#include "pythoncapi_compat.h"
59
#include <structmember.h> // T_SHORT, READONLY
610

711
#ifdef NDEBUG
812
# error "assertions must be enabled"
913
#endif
1014

11-
#ifndef MODULE_NAME
12-
# error "MODULE_NAME macro is not set"
13-
#endif
14-
1515
#define _STR(NAME) #NAME
1616
#define STR(NAME) _STR(NAME)
1717
#define _CONCAT(a, b) a ## b

0 commit comments

Comments
 (0)