Issue #27782: Fix m_methods handling in multiphase init · python/cpython@8682f57 · GitHub
Skip to content

Commit 8682f57

Browse files
committed
Issue #27782: Fix m_methods handling in multiphase init
Multi-phase extension module import now correctly allows the ``m_methods`` field to be used to add module level functions to instances of non-module types returned from ``Py_create_mod``. Patch by Xiang Zhang.
1 parent 9c8aa9b commit 8682f57

7 files changed

Lines changed: 83 additions & 32 deletions

File tree

Doc/c-api/module.rst

Lines changed: 1 addition & 1 deletion

Include/moduleobject.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ typedef struct PyModuleDef{
7777
traverseproc m_traverse;
7878
inquiry m_clear;
7979
freefunc m_free;
80-
}PyModuleDef;
80+
} PyModuleDef;
8181

8282
#ifdef __cplusplus
8383
}

Lib/test/test_importlib/extension/test_loader.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,15 @@ def test_nonmodule(self):
212212
self.assertNotEqual(type(mod), type(unittest))
213213
self.assertEqual(mod.three, 3)
214214

215+
# issue 27782
216+
def test_nonmodule_with_methods(self):
217+
'''Test creating a non-module object with methods defined'''
218+
name = self.name + '_nonmodule_with_methods'
219+
mod = self.load_module_by_name(name)
220+
self.assertNotEqual(type(mod), type(unittest))
221+
self.assertEqual(mod.three, 3)
222+
self.assertEqual(mod.bar(10, 1), 9)
223+
215224
def test_null_slots(self):
216225
'''Test that NULL slots aren't a problem'''
217226
name = self.name + '_null_slots'

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,6 +1657,7 @@ Nickolai Zeldovich
16571657
Yuxiao Zeng
16581658
Uwe Zessin
16591659
Cheng Zhang
1660+
Xiang Zhang
16601661
Kai Zhu
16611662
Tarek Ziadé
16621663
Jelle Zijlstra

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ Release date: TBA
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #27782: Multi-phase extension module import now correctly allows the
14+
``m_methods`` field to be used to add module level functions to instances
15+
of non-module types returned from ``Py_create_mod``. Patch by Xiang Zhang.
16+
1317
- Issue #27487: Warn if a submodule argument to "python -m" or
1418
runpy.run_module() is found in sys.modules after parent packages are
1519
imported, but before the submodule is executed.

Modules/_testmultiphase.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -248,14 +248,15 @@ PyInit__testmultiphase(PyObject *spec)
248248
/**** Importing a non-module object ****/
249249

250250
static PyModuleDef def_nonmodule;
251+
static PyModuleDef def_nonmodule_with_methods;
251252

252253
/* Create a SimpleNamespace(three=3) */
253254
static PyObject*
254255
createfunc_nonmodule(PyObject *spec, PyModuleDef *def)
255256
{
256257
PyObject *dct, *ns, *three;
257258

258-
if (def != &def_nonmodule) {
259+
if (def != &def_nonmodule && def != &def_nonmodule_with_methods) {
259260
PyErr_SetString(PyExc_SystemError, "def does not match");
260261
return NULL;
261262
}
@@ -291,6 +292,36 @@ PyInit__testmultiphase_nonmodule(PyObject *spec)
291292
return PyModuleDef_Init(&def_nonmodule);
292293
}
293294

295+
PyDoc_STRVAR(nonmodule_bar_doc,
296+
"bar(i,j)\n\
297+
\n\
298+
Return the difference of i - j.");
299+
300+
static PyObject *
301+
nonmodule_bar(PyObject *self, PyObject *args)
302+
{
303+
long i, j;
304+
long res;
305+
if (!PyArg_ParseTuple(args, "ll:bar", &i, &j))
306+
return NULL;
307+
res = i - j;
308+
return PyLong_FromLong(res);
309+
}
310+
311+
static PyMethodDef nonmodule_methods[] = {
312+
{"bar", nonmodule_bar, METH_VARARGS, nonmodule_bar_doc},
313+
{NULL, NULL} /* sentinel */
314+
};
315+
316+
static PyModuleDef def_nonmodule_with_methods = TEST_MODULE_DEF(
317+
"_testmultiphase_nonmodule_with_methods", slots_create_nonmodule, nonmodule_methods);
318+
319+
PyMODINIT_FUNC
320+
PyInit__testmultiphase_nonmodule_with_methods(PyObject *spec)
321+
{
322+
return PyModuleDef_Init(&def_nonmodule_with_methods);
323+
}
324+
294325
/**** Non-ASCII-named modules ****/
295326

296327
static PyModuleDef def_nonascii_latin = { \

Objects/moduleobject.c

Lines changed: 35 additions & 29 deletions

0 commit comments

Comments
 (0)