@@ -2323,6 +2323,177 @@ test_create_module_from_initfunc(void)
23232323 return Py_RunMain ();
23242324}
23252325
2326+ /// Multi-phase initialization package & submodule ///
2327+
2328+ int
2329+ mp_pkg_exec (PyObject * mod )
2330+ {
2331+ // make this a namespace package
2332+ // empty list = namespace package
2333+ if (PyModule_Add (mod , "__path__" , PyList_New (0 )) < 0 ) {
2334+ return -1 ;
2335+ }
2336+ if (PyModule_AddStringConstant (mod , "mp_pkg_exec_slot_ran" , "yes" ) < 0 ) {
2337+ return -1 ;
2338+ }
2339+ return 0 ;
2340+ }
2341+
2342+ static PyModuleDef_Slot mp_pkg_slots [] = {
2343+ {Py_mod_gil , Py_MOD_GIL_NOT_USED },
2344+ {Py_mod_exec , mp_pkg_exec },
2345+ {0 , NULL }
2346+ };
2347+
2348+ static struct PyModuleDef mp_pkg_def = {
2349+ PyModuleDef_HEAD_INIT ,
2350+ .m_name = "mp_pkg" ,
2351+ .m_size = 0 ,
2352+ .m_slots = mp_pkg_slots ,
2353+ };
2354+
2355+ PyMODINIT_FUNC
2356+ PyInit_mp_pkg (void )
2357+ {
2358+ return PyModuleDef_Init (& mp_pkg_def );
2359+ }
2360+
2361+ static PyObject *
2362+ submod_greet (PyObject * self , PyObject * Py_UNUSED (ignored ))
2363+ {
2364+ return PyUnicode_FromString ("Hello from sub-module" );
2365+ }
2366+
2367+ static PyMethodDef submod_methods [] = {
2368+ {"greet" , submod_greet , METH_NOARGS , NULL },
2369+ {NULL },
2370+ };
2371+
2372+ int
2373+ mp_submod_exec (PyObject * mod )
2374+ {
2375+ return PyModule_AddStringConstant (mod , "mp_submod_exec_slot_ran" , "yes" );
2376+ }
2377+
2378+ static PyModuleDef_Slot mp_submod_slots [] = {
2379+ {Py_mod_gil , Py_MOD_GIL_NOT_USED },
2380+ {Py_mod_exec , mp_submod_exec },
2381+ {0 , NULL }
2382+ };
2383+
2384+ static struct PyModuleDef mp_submod_def = {
2385+ PyModuleDef_HEAD_INIT ,
2386+ .m_name = "mp_pkg.mp_submod" ,
2387+ .m_size = 0 ,
2388+ .m_methods = submod_methods ,
2389+ .m_slots = mp_submod_slots ,
2390+ };
2391+
2392+ PyMODINIT_FUNC
2393+ PyInit_mp_submod (void )
2394+ {
2395+ return PyModuleDef_Init (& mp_submod_def );
2396+ }
2397+
2398+ static int
2399+ test_inittab_submodule_multiphase (void )
2400+ {
2401+ wchar_t * argv [] = {
2402+ PROGRAM_NAME ,
2403+ L"-c" ,
2404+ L"import sys;"
2405+ L"import mp_pkg.mp_submod;"
2406+ L"print(mp_pkg.mp_submod);"
2407+ L"print(sys.modules['mp_pkg.mp_submod']);"
2408+ L"print(mp_pkg.mp_submod.greet());"
2409+ L"print(f'{mp_pkg.mp_submod.mp_submod_exec_slot_ran=}');"
2410+ L"print(f'{mp_pkg.mp_pkg_exec_slot_ran=}');"
2411+ };
2412+ PyConfig config ;
2413+ if (PyImport_AppendInittab ("mp_pkg" ,
2414+ & PyInit_mp_pkg ) != 0 ) {
2415+ fprintf (stderr , "PyImport_AppendInittab() failed\n" );
2416+ return 1 ;
2417+ }
2418+ if (PyImport_AppendInittab ("mp_pkg.mp_submod" ,
2419+ & PyInit_mp_submod ) != 0 ) {
2420+ fprintf (stderr , "PyImport_AppendInittab() failed\n" );
2421+ return 1 ;
2422+ }
2423+ PyConfig_InitPythonConfig (& config );
2424+ config .isolated = 1 ;
2425+ config_set_argv (& config , Py_ARRAY_LENGTH (argv ), argv );
2426+ init_from_config_clear (& config );
2427+ return Py_RunMain ();
2428+ }
2429+
2430+ /// Single-phase initialization package & submodule ///
2431+
2432+ static struct PyModuleDef sp_pkg_def = {
2433+ PyModuleDef_HEAD_INIT ,
2434+ .m_name = "sp_pkg" ,
2435+ .m_size = 0 ,
2436+ };
2437+
2438+ PyMODINIT_FUNC
2439+ PyInit_sp_pkg (void )
2440+ {
2441+ PyObject * mod = PyModule_Create (& sp_pkg_def );
2442+ if (mod == NULL ) {
2443+ return NULL ;
2444+ }
2445+ // make this a namespace package
2446+ // empty list = namespace package
2447+ if (PyModule_Add (mod , "__path__" , PyList_New (0 )) < 0 ) {
2448+ Py_DECREF (mod );
2449+ return NULL ;
2450+ }
2451+ return mod ;
2452+ }
2453+
2454+ static struct PyModuleDef sp_submod_def = {
2455+ PyModuleDef_HEAD_INIT ,
2456+ .m_name = "sp_pkg.sp_submod" ,
2457+ .m_size = 0 ,
2458+ .m_methods = submod_methods ,
2459+ };
2460+
2461+ PyMODINIT_FUNC
2462+ PyInit_sp_submod (void )
2463+ {
2464+ return PyModule_Create (& sp_submod_def );
2465+ }
2466+
2467+ static int
2468+ test_inittab_submodule_singlephase (void )
2469+ {
2470+ wchar_t * argv [] = {
2471+ PROGRAM_NAME ,
2472+ L"-c" ,
2473+ L"import sys;"
2474+ L"import sp_pkg.sp_submod;"
2475+ L"print(sp_pkg.sp_submod);"
2476+ L"print(sys.modules['sp_pkg.sp_submod']);"
2477+ L"print(sp_pkg.sp_submod.greet());"
2478+ };
2479+ PyConfig config ;
2480+ if (PyImport_AppendInittab ("sp_pkg" ,
2481+ & PyInit_sp_pkg ) != 0 ) {
2482+ fprintf (stderr , "PyImport_AppendInittab() failed\n" );
2483+ return 1 ;
2484+ }
2485+ if (PyImport_AppendInittab ("sp_pkg.sp_submod" ,
2486+ & PyInit_sp_submod ) != 0 ) {
2487+ fprintf (stderr , "PyImport_AppendInittab() failed\n" );
2488+ return 1 ;
2489+ }
2490+ PyConfig_InitPythonConfig (& config );
2491+ config .isolated = 1 ;
2492+ config_set_argv (& config , Py_ARRAY_LENGTH (argv ), argv );
2493+ init_from_config_clear (& config );
2494+ return Py_RunMain ();
2495+ }
2496+
23262497static void wrap_allocator (PyMemAllocatorEx * allocator );
23272498static void unwrap_allocator (PyMemAllocatorEx * allocator );
23282499
@@ -2507,6 +2678,8 @@ static struct TestCase TestCases[] = {
25072678 {"test_get_incomplete_frame" , test_get_incomplete_frame },
25082679 {"test_gilstate_after_finalization" , test_gilstate_after_finalization },
25092680 {"test_create_module_from_initfunc" , test_create_module_from_initfunc },
2681+ {"test_inittab_submodule_multiphase" , test_inittab_submodule_multiphase },
2682+ {"test_inittab_submodule_singlephase" , test_inittab_submodule_singlephase },
25102683 {NULL , NULL }
25112684};
25122685
0 commit comments