Skip to content

Commit 33a05de

Browse files
[3.15] gh-157384: Set package context in PyImport_CreateModuleFromInitfunc() (GH-157387) (GH-159316)
A single-phase init function that creates its module using only the last component of a dotted name (as pybind11 does for submodules) got a module whose __name__ was the short name. Set the package context while calling the init function, as the dynamic loader does, so the module gets the full name from the spec. (cherry picked from commit 0486228) Co-authored-by: Itamar Oren <itamarost@gmail.com>
1 parent 33eb5fa commit 33a05de

5 files changed

Lines changed: 76 additions & 2 deletions

File tree

‎Doc/c-api/import.rst‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -417,6 +417,10 @@ Importing Modules
417417
(Custom importers should do this in their
418418
:py:meth:`~importlib.abc.Loader.exec_module` method.)
419419
420+
If the spec's name is dotted and *initfunc* creates the module using only
421+
the last component of the name, the module's :attr:`~module.__name__`
422+
is set to the full name from the spec.
423+
420424
On error, return NULL with an exception set.
421425
422426
.. versionadded:: 3.15

‎Lib/test/test_embed.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ def test_create_module_from_initfunc(self):
255255
"my_test_extension.exec_slot_ran='yes'\n"
256256
"<module 'embedded_ext' (static-extension)>\n"
257257
"embedded_ext.executed='yes'\n"
258+
"<module 'sp_pkg.sp_submod' (static-extension)>\n"
259+
"sp_pkg.sp_submod.__name__='sp_pkg.sp_submod'\n"
260+
"sys.modules[\"sp_pkg.sp_submod\"] is sp_pkg.sp_submod=True\n"
261+
"\"sp_submod\" in sys.modules=False\n"
258262
)
259263

260264
def test_inittab_submodule_multiphase(self):
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:c:func:`PyImport_CreateModuleFromInitfunc` now sets the package context
2+
while calling *initfunc*, so that a single-phase init submodule created with
3+
only the last component of its name gets the full name from the spec.

‎Programs/_testembed.c‎

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2405,6 +2405,40 @@ static int test_repeated_init_and_inittab(void)
24052405
return 0;
24062406
}
24072407

2408+
// A single-phase init submodule that only knows its short name,
2409+
// like pybind11's PYBIND11_MODULE(sp_submod, m) would.
2410+
static PyModuleDef cmfi_sp_submod_def = {
2411+
PyModuleDef_HEAD_INIT,
2412+
.m_name = "sp_submod",
2413+
.m_size = -1,
2414+
};
2415+
2416+
static PyObject*
2417+
PyInit_cmfi_sp_submod(void)
2418+
{
2419+
return PyModule_Create(&cmfi_sp_submod_def);
2420+
}
2421+
2422+
// A multi-phase init package for the submodule above.
2423+
static PyModuleDef_Slot cmfi_sp_pkg_slots[] = {
2424+
{Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
2425+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
2426+
{0, NULL},
2427+
};
2428+
2429+
static PyModuleDef cmfi_sp_pkg_def = {
2430+
PyModuleDef_HEAD_INIT,
2431+
.m_name = "sp_pkg",
2432+
.m_size = 0,
2433+
.m_slots = cmfi_sp_pkg_slots,
2434+
};
2435+
2436+
static PyObject*
2437+
PyInit_cmfi_sp_pkg(void)
2438+
{
2439+
return PyModuleDef_Init(&cmfi_sp_pkg_def);
2440+
}
2441+
24082442
static PyObject*
24092443
create_module(PyObject* self, PyObject* spec)
24102444
{
@@ -2420,6 +2454,14 @@ create_module(PyObject* self, PyObject* spec)
24202454
Py_DECREF(name);
24212455
return PyImport_CreateModuleFromInitfunc(spec, PyInit_embedded_ext);
24222456
}
2457+
if (PyUnicode_EqualToUTF8(name, "sp_pkg")) {
2458+
Py_DECREF(name);
2459+
return PyImport_CreateModuleFromInitfunc(spec, PyInit_cmfi_sp_pkg);
2460+
}
2461+
if (PyUnicode_EqualToUTF8(name, "sp_pkg.sp_submod")) {
2462+
Py_DECREF(name);
2463+
return PyImport_CreateModuleFromInitfunc(spec, PyInit_cmfi_sp_submod);
2464+
}
24232465
PyErr_Format(PyExc_LookupError, "static module %R not found", name);
24242466
Py_DECREF(name);
24252467
return NULL;
@@ -2467,6 +2509,12 @@ test_create_module_from_initfunc(void)
24672509
L"import embedded_ext;"
24682510
L"print(embedded_ext);"
24692511
L"print(f'{embedded_ext.executed=}');"
2512+
// Single-phase submodule whose init only knows its short name
2513+
L"import sp_pkg.sp_submod;"
2514+
L"print(sp_pkg.sp_submod);"
2515+
L"print(f'{sp_pkg.sp_submod.__name__=}');"
2516+
L"print(f'{sys.modules[\"sp_pkg.sp_submod\"] is sp_pkg.sp_submod=}');"
2517+
L"print(f'{\"sp_submod\" in sys.modules=}');"
24702518
};
24712519
PyConfig config;
24722520
if (PyImport_AppendInittab("create_static_module",
@@ -2486,8 +2534,10 @@ test_create_module_from_initfunc(void)
24862534
" _ORIGIN = \"static-extension\"\n"
24872535
" @classmethod\n"
24882536
" def find_spec(cls, fullname, path, target=None):\n"
2489-
" if fullname in {'my_test_extension', 'embedded_ext'}:\n"
2490-
" return spec_from_loader(fullname, cls, origin=cls._ORIGIN)\n"
2537+
" if fullname in {'my_test_extension', 'embedded_ext',\n"
2538+
" 'sp_pkg', 'sp_pkg.sp_submod'}:\n"
2539+
" return spec_from_loader(fullname, cls, origin=cls._ORIGIN,\n"
2540+
" is_package=(fullname == 'sp_pkg'))\n"
24912541
" return None\n"
24922542
" @staticmethod\n"
24932543
" def create_module(spec):\n"

‎Python/import.c‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,6 +2512,19 @@ create_builtin(
25122512
if (_Py_ext_module_loader_info_init_for_builtin(&info, name) < 0) {
25132513
return NULL;
25142514
}
2515+
if (initfunc != NULL) {
2516+
/* An explicitly provided init function (see
2517+
* PyImport_CreateModuleFromInitfunc()) may belong to a submodule
2518+
* whose single-phase init creates the module using only the last
2519+
* component of the name (as e.g. pybind11 does). Set the package
2520+
* context so that PyModule_Create() resolves the full name, the
2521+
* same as for dynamically loaded extensions. */
2522+
info.newcontext = PyUnicode_AsUTF8(info.name);
2523+
if (info.newcontext == NULL) {
2524+
_Py_ext_module_loader_info_clear(&info);
2525+
return NULL;
2526+
}
2527+
}
25152528

25162529
struct extensions_cache_value *cached = NULL;
25172530
PyObject *mod = import_find_extension(tstate, &info, &cached);

0 commit comments

Comments
 (0)