https://github.com/python/cpython/commit/a86963b3e2afa854132968f0d4bffe2ed2c87fe0
commit: a86963b3e2afa854132968f0d4bffe2ed2c87fe0
branch: main
author: Petr Viktorin <[email protected]>
committer: encukou <[email protected]>
date: 2026-04-02T13:54:21+02:00
summary:
gh-146636: Py_mod_abi mandatory for modules created from slots array (GH-146855)
files:
A Misc/NEWS.d/next/C_API/2026-03-31-13-33-41.gh-issue-146636.5do3wt.rst
M Doc/c-api/module.rst
M Doc/extending/first-extension-module.rst
M Doc/includes/capi-extension/spammodule-01.c
M Lib/test/test_capi/test_module.py
M Lib/test/test_cext/extension.c
M Lib/test/test_import/__init__.py
M Modules/_testcapi/module.c
M Modules/_testmultiphase.c
M Objects/moduleobject.c
M Python/modsupport.c
diff --git a/Doc/c-api/module.rst b/Doc/c-api/module.rst
index 39293b0fa228df..8b967c285ac865 100644
--- a/Doc/c-api/module.rst
+++ b/Doc/c-api/module.rst
@@ -230,6 +230,9 @@ Feature slots
When creating a module, Python checks the value of this slot
using :c:func:`PyABIInfo_Check`.
+ This slot is required, except for modules created from
+ :c:struct:`PyModuleDef`.
+
.. versionadded:: 3.15
.. c:macro:: Py_mod_multiple_interpreters
@@ -620,9 +623,9 @@ rather than from an extension's :ref:`export hook
<extension-export-hook>`.
and the :py:class:`~importlib.machinery.ModuleSpec` *spec*.
The *slots* argument must point to an array of :c:type:`PyModuleDef_Slot`
- structures, terminated by an entry slot with slot ID of 0
+ structures, terminated by an entry with slot ID of 0
(typically written as ``{0}`` or ``{0, NULL}`` in C).
- The *slots* argument may not be ``NULL``.
+ The array must include a :c:data:`Py_mod_abi` entry.
The *spec* argument may be any ``ModuleSpec``-like object, as described
in :c:macro:`Py_mod_create` documentation.
diff --git a/Doc/extending/first-extension-module.rst
b/Doc/extending/first-extension-module.rst
index f1ba0a3ceb7dba..cd755a98f7f5f4 100644
--- a/Doc/extending/first-extension-module.rst
+++ b/Doc/extending/first-extension-module.rst
@@ -265,12 +265,19 @@ Define this array just before your export hook:
.. code-block:: c
+ PyABIInfo_VAR(abi_info);
+
static PyModuleDef_Slot spam_slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, "spam"},
{Py_mod_doc, "A wonderful module with an example function"},
{0, NULL}
};
+The ``PyABIInfo_VAR(abi_info);`` macro and the :c:data:`Py_mod_abi` slot
+are a bit of boilerplate that helps prevent extensions compiled for
+a different version of Python from crashing the interpreter.
+
For both :c:data:`Py_mod_name` and :c:data:`Py_mod_doc`, the values are C
strings -- that is, NUL-terminated, UTF-8 encoded byte arrays.
diff --git a/Doc/includes/capi-extension/spammodule-01.c
b/Doc/includes/capi-extension/spammodule-01.c
index ac96f17f04712c..0bc34ef57445cb 100644
--- a/Doc/includes/capi-extension/spammodule-01.c
+++ b/Doc/includes/capi-extension/spammodule-01.c
@@ -35,7 +35,10 @@ static PyMethodDef spam_methods[] = {
/// Module slot table
+PyABIInfo_VAR(abi_info);
+
static PyModuleDef_Slot spam_slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, "spam"},
{Py_mod_doc, "A wonderful module with an example function"},
{Py_mod_methods, spam_methods},
diff --git a/Lib/test/test_capi/test_module.py
b/Lib/test/test_capi/test_module.py
index 053e6709cda42e..c32ca1098edc56 100644
--- a/Lib/test/test_capi/test_module.py
+++ b/Lib/test/test_capi/test_module.py
@@ -25,9 +25,13 @@ def def_and_token(mod):
)
class TestModFromSlotsAndSpec(unittest.TestCase):
- @requires_gil_enabled("empty slots re-enable GIL")
def test_empty(self):
- mod = _testcapi.module_from_slots_empty(FakeSpec())
+ with self.assertRaises(SystemError):
+ _testcapi.module_from_slots_empty(FakeSpec())
+
+ @requires_gil_enabled("minimal slots re-enable GIL")
+ def test_minimal(self):
+ mod = _testcapi.module_from_slots_minimal(FakeSpec())
self.assertIsInstance(mod, types.ModuleType)
self.assertEqual(def_and_token(mod), (0, 0))
self.assertEqual(mod.__name__, 'testmod')
@@ -159,6 +163,16 @@ def test_null_def_slot(self):
self.assertIn(name, str(cm.exception))
self.assertIn("NULL", str(cm.exception))
+ def test_bad_abiinfo(self):
+ """Slots that incompatible ABI is rejected"""
+ with self.assertRaises(ImportError) as cm:
+ _testcapi.module_from_bad_abiinfo(FakeSpec())
+
+ def test_multiple_abiinfo(self):
+ """Slots that Py_mod_abiinfo can be repeated"""
+ mod = _testcapi.module_from_multiple_abiinfo(FakeSpec())
+ self.assertEqual(mod.__name__, 'testmod')
+
def test_def_multiple_exec(self):
"""PyModule_Exec runs all exec slots of PyModuleDef-defined module"""
mod = _testcapi.module_from_def_multiple_exec(FakeSpec())
diff --git a/Lib/test/test_cext/extension.c b/Lib/test/test_cext/extension.c
index 7555b78f18c2e6..a880cb82811f78 100644
--- a/Lib/test/test_cext/extension.c
+++ b/Lib/test/test_cext/extension.c
@@ -119,8 +119,10 @@ _Py_COMP_DIAG_PUSH
#endif
PyDoc_STRVAR(_testcext_doc, "C test extension.");
+PyABIInfo_VAR(abi_info);
static PyModuleDef_Slot _testcext_slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, STR(MODULE_NAME)},
{Py_mod_doc, (void*)(char*)_testcext_doc},
{Py_mod_exec, (void*)_testcext_exec},
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index 437ab7031356b1..c905c0da0a1232 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -3499,12 +3499,20 @@ class Sub(tp):
pass
self.assertEqual(_testcapi.pytype_getmodulebytoken(Sub, token), module)
- @requires_gil_enabled("empty slots re-enable GIL")
def test_from_modexport_empty_slots(self):
+ # Module to test that Py_mod_abi is mandatory for PyModExport
+ modname = '_test_from_modexport_empty_slots'
+ filename = _testmultiphase.__file__
+ with self.assertRaises(SystemError):
+ import_extension_from_file(
+ modname, filename, put_in_sys_modules=False)
+
+ @requires_gil_enabled("this module re-enables GIL")
+ def test_from_modexport_minimal_slots(self):
# Module to test that:
- # - no slots are mandatory for PyModExport
+ # - no slots except Py_mod_abi is mandatory for PyModExport
# - the slots array is used as the default token
- modname = '_test_from_modexport_empty_slots'
+ modname = '_test_from_modexport_minimal_slots'
filename = _testmultiphase.__file__
module = import_extension_from_file(
modname, filename, put_in_sys_modules=False)
@@ -3516,7 +3524,7 @@ def test_from_modexport_empty_slots(self):
smoke_mod = import_extension_from_file(
'_test_from_modexport_smoke', filename, put_in_sys_modules=False)
self.assertEqual(_testcapi.pymodule_get_token(module),
- smoke_mod.get_modexport_empty_slots())
+ smoke_mod.get_modexport_minimal_slots())
@cpython_only
class TestMagicNumber(unittest.TestCase):
diff --git
a/Misc/NEWS.d/next/C_API/2026-03-31-13-33-41.gh-issue-146636.5do3wt.rst
b/Misc/NEWS.d/next/C_API/2026-03-31-13-33-41.gh-issue-146636.5do3wt.rst
new file mode 100644
index 00000000000000..8f8b832b8baee9
--- /dev/null
+++ b/Misc/NEWS.d/next/C_API/2026-03-31-13-33-41.gh-issue-146636.5do3wt.rst
@@ -0,0 +1,3 @@
+The :c:data:`Py_mod_abi` slot is now mandatory for modules created from a
+slots array (using :c:func:`PyModule_FromSlotsAndSpec` or the
+:c:func:`PyModExport_* <PyModExport_modulename>` export hook).
diff --git a/Modules/_testcapi/module.c b/Modules/_testcapi/module.c
index 3411b21e942a19..52e1d6d94a3af7 100644
--- a/Modules/_testcapi/module.c
+++ b/Modules/_testcapi/module.c
@@ -8,6 +8,8 @@
* Lib/test/test_capi/test_module.py
*/
+PyABIInfo_VAR(abi_info);
+
static PyObject *
module_from_slots_empty(PyObject *self, PyObject *spec)
{
@@ -17,6 +19,16 @@ module_from_slots_empty(PyObject *self, PyObject *spec)
return PyModule_FromSlotsAndSpec(slots, spec);
}
+static PyObject *
+module_from_slots_minimal(PyObject *self, PyObject *spec)
+{
+ PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
+ {0},
+ };
+ return PyModule_FromSlotsAndSpec(slots, spec);
+}
+
static PyObject *
module_from_slots_null(PyObject *self, PyObject *spec)
{
@@ -27,6 +39,7 @@ static PyObject *
module_from_slots_name(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, "currently ignored..."},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -39,6 +52,7 @@ static PyObject *
module_from_slots_doc(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_doc, "the docstring"},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -51,6 +65,7 @@ static PyObject *
module_from_slots_size(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_state_size, (void*)123},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -78,6 +93,7 @@ static PyObject *
module_from_slots_methods(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_methods, a_methoddef_array},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -96,6 +112,7 @@ static PyObject *
module_from_slots_gc(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_state_traverse, noop_traverse},
{Py_mod_state_clear, noop_clear},
{Py_mod_state_free, noop_free},
@@ -128,6 +145,7 @@ static PyObject *
module_from_slots_token(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_token, (void*)&test_token},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -156,6 +174,7 @@ static PyObject *
module_from_slots_exec(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_exec, simple_exec},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -189,6 +208,7 @@ static PyObject *
module_from_slots_create(PyObject *self, PyObject *spec)
{
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_create, create_attr_from_spec},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -220,6 +240,7 @@ module_from_slots_repeat_slot(PyObject *self, PyObject
*spec)
return NULL;
}
PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{slot_id, "anything"},
{slot_id, "anything else"},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
@@ -238,6 +259,7 @@ module_from_slots_null_slot(PyObject *self, PyObject *spec)
}
PyModuleDef_Slot slots[] = {
{slot_id, NULL},
+ {Py_mod_abi, &abi_info},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
{0},
@@ -254,6 +276,7 @@ module_from_def_slot(PyObject *self, PyObject *spec)
}
PyModuleDef_Slot slots[] = {
{slot_id, "anything"},
+ {Py_mod_abi, &abi_info},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
{0},
@@ -285,6 +308,7 @@ static PyModuleDef parrot_def = {
.m_slots = NULL /* set below */,
};
static PyModuleDef_Slot parrot_slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, (void*)parrot_name},
{Py_mod_doc, (void*)parrot_doc},
{Py_mod_state_size, (void*)123},
@@ -314,6 +338,43 @@ module_from_def_slot_parrot(PyObject *self, PyObject *spec)
return module;
}
+static PyObject *
+module_from_bad_abiinfo(PyObject *self, PyObject *spec)
+{
+ PyABIInfo bad_abi_info = {
+ 1, 0,
+ .abi_version=0x02080000,
+ };
+ PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
+ {Py_mod_abi, &bad_abi_info},
+ {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+ {0},
+ };
+ return PyModule_FromSlotsAndSpec(slots, spec);
+}
+
+static PyObject *
+module_from_multiple_abiinfo(PyObject *self, PyObject *spec)
+{
+ PyABIInfo extra_abi_info = {
+ 1, 0,
+ .flags=PyABIInfo_STABLE | PyABIInfo_FREETHREADING_AGNOSTIC,
+ .abi_version=0x03040000,
+ };
+ PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
+ {Py_mod_abi, &abi_info},
+ {Py_mod_abi, &extra_abi_info},
+ {Py_mod_abi, &extra_abi_info},
+ {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
+ {0},
+ };
+ return PyModule_FromSlotsAndSpec(slots, spec);
+}
+
static int
another_exec(PyObject *module)
{
@@ -344,6 +405,7 @@ static PyObject *
module_from_def_multiple_exec(PyObject *self, PyObject *spec)
{
static PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_exec, simple_exec},
{Py_mod_exec, another_exec},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
@@ -399,6 +461,7 @@ pymodule_get_state_size(PyObject *self, PyObject *module)
static PyMethodDef test_methods[] = {
{"module_from_slots_empty", module_from_slots_empty, METH_O},
+ {"module_from_slots_minimal", module_from_slots_minimal, METH_O},
{"module_from_slots_null", module_from_slots_null, METH_O},
{"module_from_slots_name", module_from_slots_name, METH_O},
{"module_from_slots_doc", module_from_slots_doc, METH_O},
@@ -413,6 +476,8 @@ static PyMethodDef test_methods[] = {
{"module_from_def_multiple_exec", module_from_def_multiple_exec, METH_O},
{"module_from_def_slot", module_from_def_slot, METH_O},
{"module_from_def_slot_parrot", module_from_def_slot_parrot, METH_O},
+ {"module_from_bad_abiinfo", module_from_bad_abiinfo, METH_O},
+ {"module_from_multiple_abiinfo", module_from_multiple_abiinfo, METH_O},
{"pymodule_get_token", pymodule_get_token, METH_O},
{"pymodule_get_def", pymodule_get_def, METH_O},
{"pymodule_get_state_size", pymodule_get_state_size, METH_O},
diff --git a/Modules/_testmultiphase.c b/Modules/_testmultiphase.c
index 4921dc90713daf..54f53c899f5e39 100644
--- a/Modules/_testmultiphase.c
+++ b/Modules/_testmultiphase.c
@@ -1034,10 +1034,13 @@ PyInit__test_no_multiple_interpreter_slot(void)
/* PyModExport_* hooks */
+PyABIInfo_VAR(abi_info);
+
PyMODEXPORT_FUNC
PyModExport__test_from_modexport(void)
{
static PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, "_test_from_modexport"},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
@@ -1050,6 +1053,7 @@ PyMODEXPORT_FUNC
PyModExport__test_from_modexport_gil_used(void)
{
static PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, "_test_from_modexport_gil_used"},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
{Py_mod_gil, Py_MOD_GIL_USED},
@@ -1100,6 +1104,7 @@ PyMODEXPORT_FUNC
PyModExport__test_from_modexport_create_nonmodule(void)
{
static PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, "_test_from_modexport_create_nonmodule"},
{Py_mod_create, modexport_create_string},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
@@ -1113,6 +1118,7 @@ PyMODEXPORT_FUNC
PyModExport__test_from_modexport_create_nonmodule_gil_used(void)
{
static PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, "_test_from_modexport_create_nonmodule"},
{Py_mod_create, modexport_create_string},
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
@@ -1132,6 +1138,18 @@ PyModExport__test_from_modexport_empty_slots(void)
return modexport_empty_slots;
}
+
+static PyModuleDef_Slot modexport_minimal_slots[] = {
+ {Py_mod_abi, &abi_info},
+ {0},
+};
+
+PyMODEXPORT_FUNC
+PyModExport__test_from_modexport_minimal_slots(void)
+{
+ return modexport_minimal_slots;
+}
+
static int
modexport_smoke_exec(PyObject *mod)
{
@@ -1172,13 +1190,13 @@ modexport_smoke_get_test_token(PyObject *mod, PyObject
*arg)
}
static PyObject *
-modexport_get_empty_slots(PyObject *mod, PyObject *arg)
+modexport_get_minimal_slots(PyObject *mod, PyObject *arg)
{
/* Get the address of modexport_empty_slots.
- * This method would be in the `_test_from_modexport_empty_slots` module,
+ * This method would be in the `_test_from_modexport_minimal_slots` module,
* if it had a methods slot.
*/
- return PyLong_FromVoidPtr(&modexport_empty_slots);
+ return PyLong_FromVoidPtr(&modexport_minimal_slots);
}
static void
@@ -1198,10 +1216,11 @@ PyModExport__test_from_modexport_smoke(void)
static PyMethodDef methods[] = {
{"get_state_int", modexport_smoke_get_state_int, METH_NOARGS},
{"get_test_token", modexport_smoke_get_test_token, METH_NOARGS},
- {"get_modexport_empty_slots", modexport_get_empty_slots, METH_NOARGS},
+ {"get_modexport_minimal_slots", modexport_get_minimal_slots,
METH_NOARGS},
{0},
};
static PyModuleDef_Slot slots[] = {
+ {Py_mod_abi, &abi_info},
{Py_mod_name, "_test_from_modexport_smoke"},
{Py_mod_doc, "the expected docstring"},
{Py_mod_exec, modexport_smoke_exec},
diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c
index e3868097c0ba9f..8339e6b91a5e16 100644
--- a/Objects/moduleobject.c
+++ b/Objects/moduleobject.c
@@ -446,6 +446,7 @@ module_from_def_and_spec(
bool seen_m_traverse_slot = false;
bool seen_m_clear_slot = false;
bool seen_m_free_slot = false;
+ bool seen_m_abi_slot = false;
for (cur_slot = def_like->m_slots; cur_slot && cur_slot->slot; cur_slot++)
{
// Macro to copy a non-NULL, non-repeatable slot.
@@ -555,6 +556,7 @@ module_from_def_and_spec(
if (PyABIInfo_Check((PyABIInfo *)cur_slot->value, name) < 0) {
goto error;
}
+ seen_m_abi_slot = true;
break;
DEF_SLOT_CASE(Py_mod_name, char*, m_name)
DEF_SLOT_CASE(Py_mod_doc, char*, m_doc)
@@ -587,6 +589,14 @@ module_from_def_and_spec(
#undef COPY_NONDEF_SLOT
#undef COPY_NONNULL_SLOT
}
+ if (!original_def && !seen_m_abi_slot) {
+ PyErr_Format(
+ PyExc_SystemError,
+ "module %s does not define Py_mod_abi,"
+ " which is mandatory for modules defined from slots only.",
+ name);
+ goto error;
+ }
#ifdef Py_GIL_DISABLED
// For modules created directly from slots (not from a def), we enable
diff --git a/Python/modsupport.c b/Python/modsupport.c
index 4624f326d17b89..bab21d1b2be5b5 100644
--- a/Python/modsupport.c
+++ b/Python/modsupport.c
@@ -735,15 +735,15 @@ int PyABIInfo_Check(PyABIInfo *info, const char
*module_name)
return _abiinfo_raise(
module_name,
"incompatible future stable ABI version (%d.%d)",
- ((info->abi_version) >> 24) % 0xff,
- ((info->abi_version) >> 16) % 0xff);
+ ((info->abi_version) >> 24) & 0xff,
+ ((info->abi_version) >> 16) & 0xff);
}
if (info->abi_version < Py_PACK_VERSION(3, 2)) {
return _abiinfo_raise(
module_name,
"invalid stable ABI version (%d.%d)",
- ((info->abi_version) >> 24) % 0xff,
- ((info->abi_version) >> 16) % 0xff);
+ ((info->abi_version) >> 24) & 0xff,
+ ((info->abi_version) >> 16) & 0xff);
}
}
if (info->flags & PyABIInfo_INTERNAL) {
@@ -758,8 +758,8 @@ int PyABIInfo_Check(PyABIInfo *info, const char
*module_name)
return _abiinfo_raise(
module_name,
"incompatible ABI version (%d.%d)",
- ((info->abi_version) >> 24) % 0xff,
- ((info->abi_version) >> 16) % 0xff);
+ ((info->abi_version) >> 24) & 0xff,
+ ((info->abi_version) >> 16) & 0xff);
}
}
}
_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]