Skip to content

Commit 6f9b080

Browse files
committed
Add/adapt tests for mandatory Py_mod_abi slot
1 parent 4e05b9b commit 6f9b080

File tree

4 files changed

+116
-10
lines changed

4 files changed

+116
-10
lines changed

Lib/test/test_capi/test_module.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@ def def_and_token(mod):
2525
)
2626

2727
class TestModFromSlotsAndSpec(unittest.TestCase):
28-
@requires_gil_enabled("empty slots re-enable GIL")
2928
def test_empty(self):
30-
mod = _testcapi.module_from_slots_empty(FakeSpec())
29+
with self.assertRaises(SystemError):
30+
_testcapi.module_from_slots_empty(FakeSpec())
31+
32+
@requires_gil_enabled("minimal slots re-enable GIL")
33+
def test_minimal(self):
34+
mod = _testcapi.module_from_slots_minimal(FakeSpec())
3135
self.assertIsInstance(mod, types.ModuleType)
3236
self.assertEqual(def_and_token(mod), (0, 0))
3337
self.assertEqual(mod.__name__, 'testmod')
@@ -159,6 +163,16 @@ def test_null_def_slot(self):
159163
self.assertIn(name, str(cm.exception))
160164
self.assertIn("NULL", str(cm.exception))
161165

166+
def test_bad_abiinfo(self):
167+
"""Slots that incompatible ABI is rejected"""
168+
with self.assertRaises(ImportError) as cm:
169+
_testcapi.module_from_bad_abiinfo(FakeSpec())
170+
171+
def test_multiple_abiinfo(self):
172+
"""Slots that Py_mod_abiinfo can be repeated"""
173+
mod = _testcapi.module_from_multiple_abiinfo(FakeSpec())
174+
self.assertEqual(mod.__name__, 'testmod')
175+
162176
def test_def_multiple_exec(self):
163177
"""PyModule_Exec runs all exec slots of PyModuleDef-defined module"""
164178
mod = _testcapi.module_from_def_multiple_exec(FakeSpec())

Lib/test/test_import/__init__.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3499,12 +3499,20 @@ class Sub(tp):
34993499
pass
35003500
self.assertEqual(_testcapi.pytype_getmodulebytoken(Sub, token), module)
35013501

3502-
@requires_gil_enabled("empty slots re-enable GIL")
35033502
def test_from_modexport_empty_slots(self):
3503+
# Module to test that Py_mod_abi is mandatory for PyModExport
3504+
modname = '_test_from_modexport_empty_slots'
3505+
filename = _testmultiphase.__file__
3506+
with self.assertRaises(SystemError):
3507+
import_extension_from_file(
3508+
modname, filename, put_in_sys_modules=False)
3509+
3510+
@requires_gil_enabled("this module re-enables GIL")
3511+
def test_from_modexport_minimal_slots(self):
35043512
# Module to test that:
3505-
# - no slots are mandatory for PyModExport
3513+
# - no slots except Py_mod_abi is mandatory for PyModExport
35063514
# - the slots array is used as the default token
3507-
modname = '_test_from_modexport_empty_slots'
3515+
modname = '_test_from_modexport_minimal_slots'
35083516
filename = _testmultiphase.__file__
35093517
module = import_extension_from_file(
35103518
modname, filename, put_in_sys_modules=False)
@@ -3516,7 +3524,7 @@ def test_from_modexport_empty_slots(self):
35163524
smoke_mod = import_extension_from_file(
35173525
'_test_from_modexport_smoke', filename, put_in_sys_modules=False)
35183526
self.assertEqual(_testcapi.pymodule_get_token(module),
3519-
smoke_mod.get_modexport_empty_slots())
3527+
smoke_mod.get_modexport_minimal_slots())
35203528

35213529
@cpython_only
35223530
class TestMagicNumber(unittest.TestCase):

Modules/_testcapi/module.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
* Lib/test/test_capi/test_module.py
99
*/
1010

11+
PyABIInfo_VAR(abi_info);
12+
1113
static PyObject *
1214
module_from_slots_empty(PyObject *self, PyObject *spec)
1315
{
@@ -17,6 +19,16 @@ module_from_slots_empty(PyObject *self, PyObject *spec)
1719
return PyModule_FromSlotsAndSpec(slots, spec);
1820
}
1921

22+
static PyObject *
23+
module_from_slots_minimal(PyObject *self, PyObject *spec)
24+
{
25+
PyModuleDef_Slot slots[] = {
26+
{Py_mod_abi, &abi_info},
27+
{0},
28+
};
29+
return PyModule_FromSlotsAndSpec(slots, spec);
30+
}
31+
2032
static PyObject *
2133
module_from_slots_null(PyObject *self, PyObject *spec)
2234
{
@@ -27,6 +39,7 @@ static PyObject *
2739
module_from_slots_name(PyObject *self, PyObject *spec)
2840
{
2941
PyModuleDef_Slot slots[] = {
42+
{Py_mod_abi, &abi_info},
3043
{Py_mod_name, "currently ignored..."},
3144
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
3245
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -39,6 +52,7 @@ static PyObject *
3952
module_from_slots_doc(PyObject *self, PyObject *spec)
4053
{
4154
PyModuleDef_Slot slots[] = {
55+
{Py_mod_abi, &abi_info},
4256
{Py_mod_doc, "the docstring"},
4357
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
4458
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -51,6 +65,7 @@ static PyObject *
5165
module_from_slots_size(PyObject *self, PyObject *spec)
5266
{
5367
PyModuleDef_Slot slots[] = {
68+
{Py_mod_abi, &abi_info},
5469
{Py_mod_state_size, (void*)123},
5570
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
5671
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -78,6 +93,7 @@ static PyObject *
7893
module_from_slots_methods(PyObject *self, PyObject *spec)
7994
{
8095
PyModuleDef_Slot slots[] = {
96+
{Py_mod_abi, &abi_info},
8197
{Py_mod_methods, a_methoddef_array},
8298
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
8399
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -96,6 +112,7 @@ static PyObject *
96112
module_from_slots_gc(PyObject *self, PyObject *spec)
97113
{
98114
PyModuleDef_Slot slots[] = {
115+
{Py_mod_abi, &abi_info},
99116
{Py_mod_state_traverse, noop_traverse},
100117
{Py_mod_state_clear, noop_clear},
101118
{Py_mod_state_free, noop_free},
@@ -128,6 +145,7 @@ static PyObject *
128145
module_from_slots_token(PyObject *self, PyObject *spec)
129146
{
130147
PyModuleDef_Slot slots[] = {
148+
{Py_mod_abi, &abi_info},
131149
{Py_mod_token, (void*)&test_token},
132150
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
133151
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -156,6 +174,7 @@ static PyObject *
156174
module_from_slots_exec(PyObject *self, PyObject *spec)
157175
{
158176
PyModuleDef_Slot slots[] = {
177+
{Py_mod_abi, &abi_info},
159178
{Py_mod_exec, simple_exec},
160179
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
161180
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -189,6 +208,7 @@ static PyObject *
189208
module_from_slots_create(PyObject *self, PyObject *spec)
190209
{
191210
PyModuleDef_Slot slots[] = {
211+
{Py_mod_abi, &abi_info},
192212
{Py_mod_create, create_attr_from_spec},
193213
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
194214
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -220,6 +240,7 @@ module_from_slots_repeat_slot(PyObject *self, PyObject *spec)
220240
return NULL;
221241
}
222242
PyModuleDef_Slot slots[] = {
243+
{Py_mod_abi, &abi_info},
223244
{slot_id, "anything"},
224245
{slot_id, "anything else"},
225246
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
@@ -238,6 +259,7 @@ module_from_slots_null_slot(PyObject *self, PyObject *spec)
238259
}
239260
PyModuleDef_Slot slots[] = {
240261
{slot_id, NULL},
262+
{Py_mod_abi, &abi_info},
241263
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
242264
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
243265
{0},
@@ -254,6 +276,7 @@ module_from_def_slot(PyObject *self, PyObject *spec)
254276
}
255277
PyModuleDef_Slot slots[] = {
256278
{slot_id, "anything"},
279+
{Py_mod_abi, &abi_info},
257280
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
258281
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
259282
{0},
@@ -285,6 +308,7 @@ static PyModuleDef parrot_def = {
285308
.m_slots = NULL /* set below */,
286309
};
287310
static PyModuleDef_Slot parrot_slots[] = {
311+
{Py_mod_abi, &abi_info},
288312
{Py_mod_name, (void*)parrot_name},
289313
{Py_mod_doc, (void*)parrot_doc},
290314
{Py_mod_state_size, (void*)123},
@@ -314,6 +338,43 @@ module_from_def_slot_parrot(PyObject *self, PyObject *spec)
314338
return module;
315339
}
316340

341+
static PyObject *
342+
module_from_bad_abiinfo(PyObject *self, PyObject *spec)
343+
{
344+
PyABIInfo bad_abi_info = {
345+
1, 0,
346+
.abi_version=0x02080000,
347+
};
348+
PyModuleDef_Slot slots[] = {
349+
{Py_mod_abi, &abi_info},
350+
{Py_mod_abi, &bad_abi_info},
351+
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
352+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
353+
{0},
354+
};
355+
return PyModule_FromSlotsAndSpec(slots, spec);
356+
}
357+
358+
static PyObject *
359+
module_from_multiple_abiinfo(PyObject *self, PyObject *spec)
360+
{
361+
PyABIInfo extra_abi_info = {
362+
1, 0,
363+
.flags=PyABIInfo_STABLE | PyABIInfo_FREETHREADING_AGNOSTIC,
364+
.abi_version=0x03040000,
365+
};
366+
PyModuleDef_Slot slots[] = {
367+
{Py_mod_abi, &abi_info},
368+
{Py_mod_abi, &abi_info},
369+
{Py_mod_abi, &extra_abi_info},
370+
{Py_mod_abi, &extra_abi_info},
371+
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
372+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
373+
{0},
374+
};
375+
return PyModule_FromSlotsAndSpec(slots, spec);
376+
}
377+
317378
static int
318379
another_exec(PyObject *module)
319380
{
@@ -344,6 +405,7 @@ static PyObject *
344405
module_from_def_multiple_exec(PyObject *self, PyObject *spec)
345406
{
346407
static PyModuleDef_Slot slots[] = {
408+
{Py_mod_abi, &abi_info},
347409
{Py_mod_exec, simple_exec},
348410
{Py_mod_exec, another_exec},
349411
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
@@ -399,6 +461,7 @@ pymodule_get_state_size(PyObject *self, PyObject *module)
399461

400462
static PyMethodDef test_methods[] = {
401463
{"module_from_slots_empty", module_from_slots_empty, METH_O},
464+
{"module_from_slots_minimal", module_from_slots_minimal, METH_O},
402465
{"module_from_slots_null", module_from_slots_null, METH_O},
403466
{"module_from_slots_name", module_from_slots_name, METH_O},
404467
{"module_from_slots_doc", module_from_slots_doc, METH_O},
@@ -413,6 +476,8 @@ static PyMethodDef test_methods[] = {
413476
{"module_from_def_multiple_exec", module_from_def_multiple_exec, METH_O},
414477
{"module_from_def_slot", module_from_def_slot, METH_O},
415478
{"module_from_def_slot_parrot", module_from_def_slot_parrot, METH_O},
479+
{"module_from_bad_abiinfo", module_from_bad_abiinfo, METH_O},
480+
{"module_from_multiple_abiinfo", module_from_multiple_abiinfo, METH_O},
416481
{"pymodule_get_token", pymodule_get_token, METH_O},
417482
{"pymodule_get_def", pymodule_get_def, METH_O},
418483
{"pymodule_get_state_size", pymodule_get_state_size, METH_O},

Modules/_testmultiphase.c

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,10 +1034,13 @@ PyInit__test_no_multiple_interpreter_slot(void)
10341034

10351035
/* PyModExport_* hooks */
10361036

1037+
PyABIInfo_VAR(abi_info);
1038+
10371039
PyMODEXPORT_FUNC
10381040
PyModExport__test_from_modexport(void)
10391041
{
10401042
static PyModuleDef_Slot slots[] = {
1043+
{Py_mod_abi, &abi_info},
10411044
{Py_mod_name, "_test_from_modexport"},
10421045
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
10431046
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -1050,6 +1053,7 @@ PyMODEXPORT_FUNC
10501053
PyModExport__test_from_modexport_gil_used(void)
10511054
{
10521055
static PyModuleDef_Slot slots[] = {
1056+
{Py_mod_abi, &abi_info},
10531057
{Py_mod_name, "_test_from_modexport_gil_used"},
10541058
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
10551059
{Py_mod_gil, Py_MOD_GIL_USED},
@@ -1100,6 +1104,7 @@ PyMODEXPORT_FUNC
11001104
PyModExport__test_from_modexport_create_nonmodule(void)
11011105
{
11021106
static PyModuleDef_Slot slots[] = {
1107+
{Py_mod_abi, &abi_info},
11031108
{Py_mod_name, "_test_from_modexport_create_nonmodule"},
11041109
{Py_mod_create, modexport_create_string},
11051110
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
@@ -1113,6 +1118,7 @@ PyMODEXPORT_FUNC
11131118
PyModExport__test_from_modexport_create_nonmodule_gil_used(void)
11141119
{
11151120
static PyModuleDef_Slot slots[] = {
1121+
{Py_mod_abi, &abi_info},
11161122
{Py_mod_name, "_test_from_modexport_create_nonmodule"},
11171123
{Py_mod_create, modexport_create_string},
11181124
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
@@ -1132,6 +1138,18 @@ PyModExport__test_from_modexport_empty_slots(void)
11321138
return modexport_empty_slots;
11331139
}
11341140

1141+
1142+
static PyModuleDef_Slot modexport_minimal_slots[] = {
1143+
{Py_mod_abi, &abi_info},
1144+
{0},
1145+
};
1146+
1147+
PyMODEXPORT_FUNC
1148+
PyModExport__test_from_modexport_minimal_slots(void)
1149+
{
1150+
return modexport_minimal_slots;
1151+
}
1152+
11351153
static int
11361154
modexport_smoke_exec(PyObject *mod)
11371155
{
@@ -1172,13 +1190,13 @@ modexport_smoke_get_test_token(PyObject *mod, PyObject *arg)
11721190
}
11731191

11741192
static PyObject *
1175-
modexport_get_empty_slots(PyObject *mod, PyObject *arg)
1193+
modexport_get_minimal_slots(PyObject *mod, PyObject *arg)
11761194
{
11771195
/* Get the address of modexport_empty_slots.
1178-
* This method would be in the `_test_from_modexport_empty_slots` module,
1196+
* This method would be in the `_test_from_modexport_minimal_slots` module,
11791197
* if it had a methods slot.
11801198
*/
1181-
return PyLong_FromVoidPtr(&modexport_empty_slots);
1199+
return PyLong_FromVoidPtr(&modexport_minimal_slots);
11821200
}
11831201

11841202
static void
@@ -1198,10 +1216,11 @@ PyModExport__test_from_modexport_smoke(void)
11981216
static PyMethodDef methods[] = {
11991217
{"get_state_int", modexport_smoke_get_state_int, METH_NOARGS},
12001218
{"get_test_token", modexport_smoke_get_test_token, METH_NOARGS},
1201-
{"get_modexport_empty_slots", modexport_get_empty_slots, METH_NOARGS},
1219+
{"get_modexport_minimal_slots", modexport_get_minimal_slots, METH_NOARGS},
12021220
{0},
12031221
};
12041222
static PyModuleDef_Slot slots[] = {
1223+
{Py_mod_abi, &abi_info},
12051224
{Py_mod_name, "_test_from_modexport_smoke"},
12061225
{Py_mod_doc, "the expected docstring"},
12071226
{Py_mod_exec, modexport_smoke_exec},

0 commit comments

Comments
 (0)