gh-107954, PEP 741: Add PyInitConfig_AddModule() function (#123668)

This commit is contained in:
Victor Stinner 2024-09-04 13:41:57 +02:00 committed by GitHub
parent 7d2c2f24da
commit 0d6b6e34a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 121 additions and 0 deletions

View File

@ -1744,6 +1744,26 @@ only implemented when ``Py_InitializeFromInitConfig()`` is called, not by the
* Set an error in *config* and return ``-1`` on error. * Set an error in *config* and return ``-1`` on error.
Module
------
.. c:function:: int PyInitConfig_AddModule(PyInitConfig *config, const char *name, PyObject* (*initfunc)(void))
Add a built-in extension module to the table of built-in modules.
The new module can be imported by the name *name*, and uses the function
*initfunc* as the initialization function called on the first attempted
import.
* Return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
If Python is initialized multiple times, ``PyInitConfig_AddModule()`` must
be called at each Python initialization.
Similar to the :c:func:`PyImport_AppendInittab` function.
Initialize Python Initialize Python
----------------- -----------------

View File

@ -524,6 +524,7 @@ New Features
* :c:func:`PyInitConfig_SetInt` * :c:func:`PyInitConfig_SetInt`
* :c:func:`PyInitConfig_SetStr` * :c:func:`PyInitConfig_SetStr`
* :c:func:`PyInitConfig_SetStrList` * :c:func:`PyInitConfig_SetStrList`
* :c:func:`PyInitConfig_AddModule`
* :c:func:`Py_InitializeFromInitConfig` * :c:func:`Py_InitializeFromInitConfig`
(Contributed by Victor Stinner in :gh:`107954`.) (Contributed by Victor Stinner in :gh:`107954`.)

View File

@ -313,6 +313,10 @@ PyAPI_FUNC(int) PyInitConfig_SetStrList(PyInitConfig *config,
size_t length, size_t length,
char * const *items); char * const *items);
PyAPI_FUNC(int) PyInitConfig_AddModule(PyInitConfig *config,
const char *name,
PyObject* (*initfunc)(void));
PyAPI_FUNC(int) Py_InitializeFromInitConfig(PyInitConfig *config); PyAPI_FUNC(int) Py_InitializeFromInitConfig(PyInitConfig *config);

View File

@ -1775,6 +1775,9 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
def test_initconfig_exit(self): def test_initconfig_exit(self):
self.run_embedded_interpreter("test_initconfig_exit") self.run_embedded_interpreter("test_initconfig_exit")
def test_initconfig_module(self):
self.run_embedded_interpreter("test_initconfig_module")
def test_get_argc_argv(self): def test_get_argc_argv(self):
self.run_embedded_interpreter("test_get_argc_argv") self.run_embedded_interpreter("test_get_argc_argv")
# ignore output # ignore output

View File

@ -12,6 +12,7 @@ Add functions to configure the Python initialization (:pep:`741`):
* :c:func:`PyInitConfig_SetInt` * :c:func:`PyInitConfig_SetInt`
* :c:func:`PyInitConfig_SetStr` * :c:func:`PyInitConfig_SetStr`
* :c:func:`PyInitConfig_SetStrList` * :c:func:`PyInitConfig_SetStrList`
* :c:func:`PyInitConfig_AddModule`
* :c:func:`Py_InitializeFromInitConfig` * :c:func:`Py_InitializeFromInitConfig`
Patch by Victor Stinner. Patch by Victor Stinner.

View File

@ -1963,6 +1963,62 @@ static int test_initconfig_exit(void)
} }
static PyModuleDef_Slot extension_slots[] = {
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
{0, NULL}
};
static struct PyModuleDef extension_module = {
PyModuleDef_HEAD_INIT,
.m_name = "my_test_extension",
.m_size = 0,
.m_slots = extension_slots,
};
static PyObject* init_my_test_extension(void)
{
return PyModuleDef_Init(&extension_module);
}
static int test_initconfig_module(void)
{
PyInitConfig *config = PyInitConfig_Create();
if (config == NULL) {
printf("Init allocation error\n");
return 1;
}
if (PyInitConfig_SetStr(config, "program_name", PROGRAM_NAME_UTF8) < 0) {
goto error;
}
if (PyInitConfig_AddModule(config, "my_test_extension",
init_my_test_extension) < 0) {
goto error;
}
if (Py_InitializeFromInitConfig(config) < 0) {
goto error;
}
PyInitConfig_Free(config);
if (PyRun_SimpleString("import my_test_extension") < 0) {
fprintf(stderr, "unable to import my_test_extension\n");
exit(1);
}
Py_Finalize();
return 0;
const char *err_msg;
error:
(void)PyInitConfig_GetError(config, &err_msg);
printf("Python init failed: %s\n", err_msg);
exit(1);
}
static void configure_init_main(PyConfig *config) static void configure_init_main(PyConfig *config)
{ {
wchar_t* argv[] = { wchar_t* argv[] = {
@ -2384,6 +2440,7 @@ static struct TestCase TestCases[] = {
{"test_initconfig_api", test_initconfig_api}, {"test_initconfig_api", test_initconfig_api},
{"test_initconfig_get_api", test_initconfig_get_api}, {"test_initconfig_get_api", test_initconfig_get_api},
{"test_initconfig_exit", test_initconfig_exit}, {"test_initconfig_exit", test_initconfig_exit},
{"test_initconfig_module", test_initconfig_module},
{"test_run_main", test_run_main}, {"test_run_main", test_run_main},
{"test_run_main_loop", test_run_main_loop}, {"test_run_main_loop", test_run_main_loop},
{"test_get_argc_argv", test_get_argc_argv}, {"test_get_argc_argv", test_get_argc_argv},

View File

@ -3423,6 +3423,8 @@ _Py_DumpPathConfig(PyThreadState *tstate)
struct PyInitConfig { struct PyInitConfig {
PyPreConfig preconfig; PyPreConfig preconfig;
PyConfig config; PyConfig config;
struct _inittab *inittab;
Py_ssize_t inittab_size;
PyStatus status; PyStatus status;
char *err_msg; char *err_msg;
}; };
@ -3873,9 +3875,42 @@ PyInitConfig_SetStrList(PyInitConfig *config, const char *name,
} }
int
PyInitConfig_AddModule(PyInitConfig *config, const char *name,
PyObject* (*initfunc)(void))
{
size_t size = sizeof(struct _inittab) * (config->inittab_size + 2);
struct _inittab *new_inittab = PyMem_RawRealloc(config->inittab, size);
if (new_inittab == NULL) {
config->status = _PyStatus_NO_MEMORY();
return -1;
}
config->inittab = new_inittab;
struct _inittab *entry = &config->inittab[config->inittab_size];
entry->name = name;
entry->initfunc = initfunc;
// Terminator entry
entry = &config->inittab[config->inittab_size + 1];
entry->name = NULL;
entry->initfunc = NULL;
config->inittab_size++;
return 0;
}
int int
Py_InitializeFromInitConfig(PyInitConfig *config) Py_InitializeFromInitConfig(PyInitConfig *config)
{ {
if (config->inittab_size >= 1) {
if (PyImport_ExtendInittab(config->inittab) < 0) {
config->status = _PyStatus_NO_MEMORY();
return -1;
}
}
_PyPreConfig_GetConfig(&config->preconfig, &config->config); _PyPreConfig_GetConfig(&config->preconfig, &config->config);
config->status = Py_PreInitializeFromArgs( config->status = Py_PreInitializeFromArgs(