@@ -214,6 +214,8 @@ static PyModuleDef embedded_ext = {
214214static PyObject *
215215PyInit_embedded_ext (void )
216216{
217+ // keep this as a single-phase initialization module;
218+ // see test_create_module_from_initfunc
217219 return PyModule_Create (& embedded_ext );
218220}
219221
@@ -1980,8 +1982,16 @@ static int test_initconfig_exit(void)
19801982}
19811983
19821984
1985+ int
1986+ extension_module_exec (PyObject * mod )
1987+ {
1988+ return PyModule_AddStringConstant (mod , "exec_slot_ran" , "yes" );
1989+ }
1990+
1991+
19831992static PyModuleDef_Slot extension_slots [] = {
19841993 {Py_mod_gil , Py_MOD_GIL_NOT_USED },
1994+ {Py_mod_exec , extension_module_exec },
19851995 {0 , NULL }
19861996};
19871997
@@ -2380,6 +2390,160 @@ static int test_repeated_init_and_inittab(void)
23802390 return 0 ;
23812391}
23822392
2393+ // A single-phase init submodule that only knows its short name,
2394+ // like pybind11's PYBIND11_MODULE(sp_submod, m) would.
2395+ static PyModuleDef cmfi_sp_submod_def = {
2396+ PyModuleDef_HEAD_INIT ,
2397+ .m_name = "sp_submod" ,
2398+ .m_size = -1 ,
2399+ };
2400+
2401+ static PyObject *
2402+ PyInit_cmfi_sp_submod (void )
2403+ {
2404+ return PyModule_Create (& cmfi_sp_submod_def );
2405+ }
2406+
2407+ // A multi-phase init package for the submodule above.
2408+ static PyModuleDef_Slot cmfi_sp_pkg_slots [] = {
2409+ {Py_mod_multiple_interpreters , Py_MOD_PER_INTERPRETER_GIL_SUPPORTED },
2410+ {Py_mod_gil , Py_MOD_GIL_NOT_USED },
2411+ {0 , NULL },
2412+ };
2413+
2414+ static PyModuleDef cmfi_sp_pkg_def = {
2415+ PyModuleDef_HEAD_INIT ,
2416+ .m_name = "sp_pkg" ,
2417+ .m_size = 0 ,
2418+ .m_slots = cmfi_sp_pkg_slots ,
2419+ };
2420+
2421+ static PyObject *
2422+ PyInit_cmfi_sp_pkg (void )
2423+ {
2424+ return PyModuleDef_Init (& cmfi_sp_pkg_def );
2425+ }
2426+
2427+ static PyObject *
2428+ create_module (PyObject * self , PyObject * spec )
2429+ {
2430+ PyObject * name = PyObject_GetAttrString (spec , "name" );
2431+ if (!name ) {
2432+ return NULL ;
2433+ }
2434+ if (PyUnicode_EqualToUTF8 (name , "my_test_extension" )) {
2435+ Py_DECREF (name );
2436+ return PyImport_CreateModuleFromInitfunc (spec , init_my_test_extension );
2437+ }
2438+ if (PyUnicode_EqualToUTF8 (name , "embedded_ext" )) {
2439+ Py_DECREF (name );
2440+ return PyImport_CreateModuleFromInitfunc (spec , PyInit_embedded_ext );
2441+ }
2442+ if (PyUnicode_EqualToUTF8 (name , "sp_pkg" )) {
2443+ Py_DECREF (name );
2444+ return PyImport_CreateModuleFromInitfunc (spec , PyInit_cmfi_sp_pkg );
2445+ }
2446+ if (PyUnicode_EqualToUTF8 (name , "sp_pkg.sp_submod" )) {
2447+ Py_DECREF (name );
2448+ return PyImport_CreateModuleFromInitfunc (spec , PyInit_cmfi_sp_submod );
2449+ }
2450+ PyErr_Format (PyExc_LookupError , "static module %R not found" , name );
2451+ Py_DECREF (name );
2452+ return NULL ;
2453+ }
2454+
2455+ static PyObject *
2456+ exec_module (PyObject * self , PyObject * mod )
2457+ {
2458+ PyModuleDef * def = PyModule_GetDef (mod );
2459+ if (def == NULL ) {
2460+ return NULL ;
2461+ }
2462+ if (PyModule_ExecDef (mod , def ) < 0 ) {
2463+ return NULL ;
2464+ }
2465+ Py_RETURN_NONE ;
2466+ }
2467+
2468+ static PyMethodDef create_static_module_methods [] = {
2469+ {"create_module" , create_module , METH_O , NULL },
2470+ {"exec_module" , exec_module , METH_O , NULL },
2471+ {NULL }
2472+ };
2473+
2474+ static struct PyModuleDef create_static_module_def = {
2475+ PyModuleDef_HEAD_INIT ,
2476+ .m_name = "create_static_module" ,
2477+ .m_size = 0 ,
2478+ .m_methods = create_static_module_methods ,
2479+ .m_slots = extension_slots ,
2480+ };
2481+
2482+ PyMODINIT_FUNC PyInit_create_static_module (void ) {
2483+ return PyModuleDef_Init (& create_static_module_def );
2484+ }
2485+
2486+ static int
2487+ test_create_module_from_initfunc (void )
2488+ {
2489+ wchar_t * argv [] = {
2490+ PROGRAM_NAME ,
2491+ L"-c" ,
2492+ // Multi-phase initialization
2493+ L"import my_test_extension;"
2494+ L"print(my_test_extension);"
2495+ L"print(f'{my_test_extension.executed=}');"
2496+ L"print(f'{my_test_extension.exec_slot_ran=}');"
2497+ // Single-phase initialization
2498+ L"import embedded_ext;"
2499+ L"print(embedded_ext);"
2500+ L"print(f'{embedded_ext.executed=}');"
2501+ // Single-phase submodule whose init only knows its short name
2502+ L"import sp_pkg.sp_submod;"
2503+ L"print(sp_pkg.sp_submod);"
2504+ L"print(f'{sp_pkg.sp_submod.__name__=}');"
2505+ L"print(f'{sys.modules[\"sp_pkg.sp_submod\"] is sp_pkg.sp_submod=}');"
2506+ L"print(f'{\"sp_submod\" in sys.modules=}');"
2507+ };
2508+ PyConfig config ;
2509+ if (PyImport_AppendInittab ("create_static_module" ,
2510+ & PyInit_create_static_module ) != 0 ) {
2511+ fprintf (stderr , "PyImport_AppendInittab() failed\n" );
2512+ return 1 ;
2513+ }
2514+ PyConfig_InitPythonConfig (& config );
2515+ config .isolated = 1 ;
2516+ config_set_argv (& config , Py_ARRAY_LENGTH (argv ), argv );
2517+ init_from_config_clear (& config );
2518+ int result = PyRun_SimpleString (
2519+ "import sys\n"
2520+ "from importlib.util import spec_from_loader\n"
2521+ "import create_static_module\n"
2522+ "class StaticExtensionImporter:\n"
2523+ " _ORIGIN = \"static-extension\"\n"
2524+ " @classmethod\n"
2525+ " def find_spec(cls, fullname, path, target=None):\n"
2526+ " if fullname in {'my_test_extension', 'embedded_ext',\n"
2527+ " 'sp_pkg', 'sp_pkg.sp_submod'}:\n"
2528+ " return spec_from_loader(fullname, cls, origin=cls._ORIGIN,\n"
2529+ " is_package=(fullname == 'sp_pkg'))\n"
2530+ " return None\n"
2531+ " @staticmethod\n"
2532+ " def create_module(spec):\n"
2533+ " return create_static_module.create_module(spec)\n"
2534+ " @staticmethod\n"
2535+ " def exec_module(module):\n"
2536+ " create_static_module.exec_module(module)\n"
2537+ " module.executed = 'yes'\n"
2538+ "sys.meta_path.append(StaticExtensionImporter)\n"
2539+ );
2540+ if (result < 0 ) {
2541+ fprintf (stderr , "PyRun_SimpleString() failed\n" );
2542+ return 1 ;
2543+ }
2544+ return Py_RunMain ();
2545+ }
2546+
23832547/// Multi-phase initialization package & submodule ///
23842548
23852549int
@@ -2714,6 +2878,7 @@ static struct TestCase TestCases[] = {
27142878 {"test_get_incomplete_frame" , test_get_incomplete_frame },
27152879 {"test_inittab_submodule_multiphase" , test_inittab_submodule_multiphase },
27162880 {"test_inittab_submodule_singlephase" , test_inittab_submodule_singlephase },
2881+ {"test_create_module_from_initfunc" , test_create_module_from_initfunc },
27172882 {NULL , NULL }
27182883};
27192884
0 commit comments