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