gh-116146: Add C-API to create module from spec and initfunc (GH-139196)
Co-authored-by: Kumar Aditya <kumaraditya@python.org> Co-authored-by: Petr Viktorin <encukou@gmail.com> Co-authored-by: Victor Stinner <vstinner@python.org>
This commit is contained in:
@@ -166,6 +166,8 @@ static PyModuleDef embedded_ext = {
|
||||
static PyObject*
|
||||
PyInit_embedded_ext(void)
|
||||
{
|
||||
// keep this as a single-phase initialization module;
|
||||
// see test_create_module_from_initfunc
|
||||
return PyModule_Create(&embedded_ext);
|
||||
}
|
||||
|
||||
@@ -1894,8 +1896,16 @@ static int test_initconfig_exit(void)
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
extension_module_exec(PyObject *mod)
|
||||
{
|
||||
return PyModule_AddStringConstant(mod, "exec_slot_ran", "yes");
|
||||
}
|
||||
|
||||
|
||||
static PyModuleDef_Slot extension_slots[] = {
|
||||
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
||||
{Py_mod_exec, extension_module_exec},
|
||||
{0, NULL}
|
||||
};
|
||||
|
||||
@@ -2213,6 +2223,106 @@ static int test_repeated_init_and_inittab(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
create_module(PyObject* self, PyObject* spec)
|
||||
{
|
||||
PyObject *name = PyObject_GetAttrString(spec, "name");
|
||||
if (!name) {
|
||||
return NULL;
|
||||
}
|
||||
if (PyUnicode_EqualToUTF8(name, "my_test_extension")) {
|
||||
Py_DECREF(name);
|
||||
return PyImport_CreateModuleFromInitfunc(spec, init_my_test_extension);
|
||||
}
|
||||
if (PyUnicode_EqualToUTF8(name, "embedded_ext")) {
|
||||
Py_DECREF(name);
|
||||
return PyImport_CreateModuleFromInitfunc(spec, PyInit_embedded_ext);
|
||||
}
|
||||
PyErr_Format(PyExc_LookupError, "static module %R not found", name);
|
||||
Py_DECREF(name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
exec_module(PyObject* self, PyObject* mod)
|
||||
{
|
||||
if (PyModule_Exec(mod) < 0) {
|
||||
return NULL;
|
||||
}
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMethodDef create_static_module_methods[] = {
|
||||
{"create_module", create_module, METH_O, NULL},
|
||||
{"exec_module", exec_module, METH_O, NULL},
|
||||
{}
|
||||
};
|
||||
|
||||
static struct PyModuleDef create_static_module_def = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
.m_name = "create_static_module",
|
||||
.m_size = 0,
|
||||
.m_methods = create_static_module_methods,
|
||||
.m_slots = extension_slots,
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC PyInit_create_static_module(void) {
|
||||
return PyModuleDef_Init(&create_static_module_def);
|
||||
}
|
||||
|
||||
static int
|
||||
test_create_module_from_initfunc(void)
|
||||
{
|
||||
wchar_t* argv[] = {
|
||||
PROGRAM_NAME,
|
||||
L"-c",
|
||||
// Multi-phase initialization
|
||||
L"import my_test_extension;"
|
||||
L"print(my_test_extension);"
|
||||
L"print(f'{my_test_extension.executed=}');"
|
||||
L"print(f'{my_test_extension.exec_slot_ran=}');"
|
||||
// Single-phase initialization
|
||||
L"import embedded_ext;"
|
||||
L"print(embedded_ext);"
|
||||
L"print(f'{embedded_ext.executed=}');"
|
||||
};
|
||||
PyConfig config;
|
||||
if (PyImport_AppendInittab("create_static_module",
|
||||
&PyInit_create_static_module) != 0) {
|
||||
fprintf(stderr, "PyImport_AppendInittab() failed\n");
|
||||
return 1;
|
||||
}
|
||||
PyConfig_InitPythonConfig(&config);
|
||||
config.isolated = 1;
|
||||
config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv);
|
||||
init_from_config_clear(&config);
|
||||
int result = PyRun_SimpleString(
|
||||
"import sys\n"
|
||||
"from importlib.util import spec_from_loader\n"
|
||||
"import create_static_module\n"
|
||||
"class StaticExtensionImporter:\n"
|
||||
" _ORIGIN = \"static-extension\"\n"
|
||||
" @classmethod\n"
|
||||
" def find_spec(cls, fullname, path, target=None):\n"
|
||||
" if fullname in {'my_test_extension', 'embedded_ext'}:\n"
|
||||
" return spec_from_loader(fullname, cls, origin=cls._ORIGIN)\n"
|
||||
" return None\n"
|
||||
" @staticmethod\n"
|
||||
" def create_module(spec):\n"
|
||||
" return create_static_module.create_module(spec)\n"
|
||||
" @staticmethod\n"
|
||||
" def exec_module(module):\n"
|
||||
" create_static_module.exec_module(module)\n"
|
||||
" module.executed = 'yes'\n"
|
||||
"sys.meta_path.append(StaticExtensionImporter)\n"
|
||||
);
|
||||
if (result < 0) {
|
||||
fprintf(stderr, "PyRun_SimpleString() failed\n");
|
||||
return 1;
|
||||
}
|
||||
return Py_RunMain();
|
||||
}
|
||||
|
||||
static void wrap_allocator(PyMemAllocatorEx *allocator);
|
||||
static void unwrap_allocator(PyMemAllocatorEx *allocator);
|
||||
|
||||
@@ -2396,6 +2506,7 @@ static struct TestCase TestCases[] = {
|
||||
#endif
|
||||
{"test_get_incomplete_frame", test_get_incomplete_frame},
|
||||
{"test_gilstate_after_finalization", test_gilstate_after_finalization},
|
||||
{"test_create_module_from_initfunc", test_create_module_from_initfunc},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user