@@ -166,6 +166,8 @@ static PyModuleDef embedded_ext = {
166166static PyObject *
167167PyInit_embedded_ext (void )
168168{
169+ // keep this as a single-phase initialization module;
170+ // see test_create_module_from_initfunc
169171 return PyModule_Create (& embedded_ext );
170172}
171173
@@ -1894,8 +1896,16 @@ static int test_initconfig_exit(void)
18941896}
18951897
18961898
1899+ int
1900+ extension_module_exec (PyObject * mod )
1901+ {
1902+ return PyModule_AddStringConstant (mod , "exec_slot_ran" , "yes" );
1903+ }
1904+
1905+
18971906static PyModuleDef_Slot extension_slots [] = {
18981907 {Py_mod_gil , Py_MOD_GIL_NOT_USED },
1908+ {Py_mod_exec , extension_module_exec },
18991909 {0 , NULL }
19001910};
19011911
@@ -2213,6 +2223,106 @@ static int test_repeated_init_and_inittab(void)
22132223 return 0 ;
22142224}
22152225
2226+ static PyObject *
2227+ create_module (PyObject * self , PyObject * spec )
2228+ {
2229+ PyObject * name = PyObject_GetAttrString (spec , "name" );
2230+ if (!name ) {
2231+ return NULL ;
2232+ }
2233+ if (PyUnicode_EqualToUTF8 (name , "my_test_extension" )) {
2234+ Py_DECREF (name );
2235+ return PyImport_CreateModuleFromInitfunc (spec , init_my_test_extension );
2236+ }
2237+ if (PyUnicode_EqualToUTF8 (name , "embedded_ext" )) {
2238+ Py_DECREF (name );
2239+ return PyImport_CreateModuleFromInitfunc (spec , PyInit_embedded_ext );
2240+ }
2241+ PyErr_Format (PyExc_LookupError , "static module %R not found" , name );
2242+ Py_DECREF (name );
2243+ return NULL ;
2244+ }
2245+
2246+ static PyObject *
2247+ exec_module (PyObject * self , PyObject * mod )
2248+ {
2249+ if (PyModule_Exec (mod ) < 0 ) {
2250+ return NULL ;
2251+ }
2252+ Py_RETURN_NONE ;
2253+ }
2254+
2255+ static PyMethodDef create_static_module_methods [] = {
2256+ {"create_module" , create_module , METH_O , NULL },
2257+ {"exec_module" , exec_module , METH_O , NULL },
2258+ {}
2259+ };
2260+
2261+ static struct PyModuleDef create_static_module_def = {
2262+ PyModuleDef_HEAD_INIT ,
2263+ .m_name = "create_static_module" ,
2264+ .m_size = 0 ,
2265+ .m_methods = create_static_module_methods ,
2266+ .m_slots = extension_slots ,
2267+ };
2268+
2269+ PyMODINIT_FUNC PyInit_create_static_module (void ) {
2270+ return PyModuleDef_Init (& create_static_module_def );
2271+ }
2272+
2273+ static int
2274+ test_create_module_from_initfunc (void )
2275+ {
2276+ wchar_t * argv [] = {
2277+ PROGRAM_NAME ,
2278+ L"-c" ,
2279+ // Multi-phase initialization
2280+ L"import my_test_extension;"
2281+ L"print(my_test_extension);"
2282+ L"print(f'{my_test_extension.executed=}');"
2283+ L"print(f'{my_test_extension.exec_slot_ran=}');"
2284+ // Single-phase initialization
2285+ L"import embedded_ext;"
2286+ L"print(embedded_ext);"
2287+ L"print(f'{embedded_ext.executed=}');"
2288+ };
2289+ PyConfig config ;
2290+ if (PyImport_AppendInittab ("create_static_module" ,
2291+ & PyInit_create_static_module ) != 0 ) {
2292+ fprintf (stderr , "PyImport_AppendInittab() failed\n" );
2293+ return 1 ;
2294+ }
2295+ PyConfig_InitPythonConfig (& config );
2296+ config .isolated = 1 ;
2297+ config_set_argv (& config , Py_ARRAY_LENGTH (argv ), argv );
2298+ init_from_config_clear (& config );
2299+ int result = PyRun_SimpleString (
2300+ "import sys\n"
2301+ "from importlib.util import spec_from_loader\n"
2302+ "import create_static_module\n"
2303+ "class StaticExtensionImporter:\n"
2304+ " _ORIGIN = \"static-extension\"\n"
2305+ " @classmethod\n"
2306+ " def find_spec(cls, fullname, path, target=None):\n"
2307+ " if fullname in {'my_test_extension', 'embedded_ext'}:\n"
2308+ " return spec_from_loader(fullname, cls, origin=cls._ORIGIN)\n"
2309+ " return None\n"
2310+ " @staticmethod\n"
2311+ " def create_module(spec):\n"
2312+ " return create_static_module.create_module(spec)\n"
2313+ " @staticmethod\n"
2314+ " def exec_module(module):\n"
2315+ " create_static_module.exec_module(module)\n"
2316+ " module.executed = 'yes'\n"
2317+ "sys.meta_path.append(StaticExtensionImporter)\n"
2318+ );
2319+ if (result < 0 ) {
2320+ fprintf (stderr , "PyRun_SimpleString() failed\n" );
2321+ return 1 ;
2322+ }
2323+ return Py_RunMain ();
2324+ }
2325+
22162326static void wrap_allocator (PyMemAllocatorEx * allocator );
22172327static void unwrap_allocator (PyMemAllocatorEx * allocator );
22182328
@@ -2396,6 +2506,7 @@ static struct TestCase TestCases[] = {
23962506#endif
23972507 {"test_get_incomplete_frame" , test_get_incomplete_frame },
23982508 {"test_gilstate_after_finalization" , test_gilstate_after_finalization },
2509+ {"test_create_module_from_initfunc" , test_create_module_from_initfunc },
23992510 {NULL , NULL }
24002511};
24012512
0 commit comments