Skip to content

Commit 3a62386

Browse files
committed
pythongh-107954, PEP 741: Add PyInitConfig_AddModule() function
1 parent 68fe575 commit 3a62386

File tree

7 files changed

+121
-0
lines changed

7 files changed

+121
-0
lines changed

Doc/c-api/init_config.rst

+20
Original file line numberDiff line numberDiff line change
@@ -1744,6 +1744,26 @@ only implemented when ``Py_InitializeFromInitConfig()`` is called, not by the
17441744
* Set an error in *config* and return ``-1`` on error.
17451745
17461746
1747+
Module
1748+
------
1749+
1750+
.. c:function:: int PyInitConfig_AddModule(PyInitConfig *config, const char *name, PyObject* (*initfunc)(void))
1751+
1752+
Add a built-in extension module to the table of built-in modules.
1753+
1754+
The new module can be imported by the name *name*, and uses the function
1755+
*initfunc* as the initialization function called on the first attempted
1756+
import.
1757+
1758+
* Return ``0`` on success.
1759+
* Set an error in config and return ``-1`` on error.
1760+
1761+
If Python is initialized multiple times, ``PyInitConfig_AddModule()`` must
1762+
be called at each Python initialization.
1763+
1764+
Similar to the :c:func:`PyImport_AppendInittab` function.
1765+
1766+
17471767
Initialize Python
17481768
-----------------
17491769

Doc/whatsnew/3.14.rst

+1
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,7 @@ New Features
524524
* :c:func:`PyInitConfig_SetInt`
525525
* :c:func:`PyInitConfig_SetStr`
526526
* :c:func:`PyInitConfig_SetStrList`
527+
* :c:func:`PyInitConfig_AddModule`
527528
* :c:func:`Py_InitializeFromInitConfig`
528529

529530
(Contributed by Victor Stinner in :gh:`107954`.)

Include/cpython/initconfig.h

+4
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,10 @@ PyAPI_FUNC(int) PyInitConfig_SetStrList(PyInitConfig *config,
313313
size_t length,
314314
char * const *items);
315315

316+
PyAPI_FUNC(int) PyInitConfig_AddModule(PyInitConfig *config,
317+
const char *name,
318+
PyObject* (*initfunc)(void));
319+
316320
PyAPI_FUNC(int) Py_InitializeFromInitConfig(PyInitConfig *config);
317321

318322

Lib/test/test_embed.py

+3
Original file line numberDiff line numberDiff line change
@@ -1776,6 +1776,9 @@ def test_initconfig_get_api(self):
17761776
def test_initconfig_exit(self):
17771777
self.run_embedded_interpreter("test_initconfig_exit")
17781778

1779+
def test_initconfig_module(self):
1780+
self.run_embedded_interpreter("test_initconfig_module")
1781+
17791782
def test_get_argc_argv(self):
17801783
self.run_embedded_interpreter("test_get_argc_argv")
17811784
# ignore output

Misc/NEWS.d/next/C_API/2024-08-30-14-02-17.gh-issue-107954.TPvj4u.rst

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Add functions to configure the Python initialization (:pep:`741`):
1212
* :c:func:`PyInitConfig_SetInt`
1313
* :c:func:`PyInitConfig_SetStr`
1414
* :c:func:`PyInitConfig_SetStrList`
15+
* :c:func:`PyInitConfig_AddModule`
1516
* :c:func:`Py_InitializeFromInitConfig`
1617

1718
Patch by Victor Stinner.

Programs/_testembed.c

+57
Original file line numberDiff line numberDiff line change
@@ -1941,6 +1941,62 @@ static int test_initconfig_exit(void)
19411941
}
19421942

19431943

1944+
static PyModuleDef_Slot extension_slots[] = {
1945+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
1946+
{0, NULL}
1947+
};
1948+
1949+
static struct PyModuleDef extension_module = {
1950+
PyModuleDef_HEAD_INIT,
1951+
.m_name = "my_test_extension",
1952+
.m_size = 0,
1953+
.m_slots = extension_slots,
1954+
};
1955+
1956+
static PyObject* init_my_test_extension(void)
1957+
{
1958+
return PyModuleDef_Init(&extension_module);
1959+
}
1960+
1961+
1962+
static int test_initconfig_module(void)
1963+
{
1964+
PyInitConfig *config = PyInitConfig_Create();
1965+
if (config == NULL) {
1966+
printf("Init allocation error\n");
1967+
return 1;
1968+
}
1969+
1970+
if (PyInitConfig_SetStr(config, "program_name", PROGRAM_NAME_UTF8) < 0) {
1971+
goto error;
1972+
}
1973+
1974+
if (PyInitConfig_AddModule(config, "my_test_extension",
1975+
init_my_test_extension) < 0) {
1976+
goto error;
1977+
}
1978+
1979+
if (Py_InitializeFromInitConfig(config) < 0) {
1980+
goto error;
1981+
}
1982+
PyInitConfig_Free(config);
1983+
1984+
if (PyRun_SimpleString("import my_test_extension") < 0) {
1985+
fprintf(stderr, "unable to import my_test_extension\n");
1986+
exit(1);
1987+
}
1988+
1989+
Py_Finalize();
1990+
return 0;
1991+
1992+
const char *err_msg;
1993+
error:
1994+
(void)PyInitConfig_GetError(config, &err_msg);
1995+
printf("Python init failed: %s\n", err_msg);
1996+
exit(1);
1997+
}
1998+
1999+
19442000
static void configure_init_main(PyConfig *config)
19452001
{
19462002
wchar_t* argv[] = {
@@ -2362,6 +2418,7 @@ static struct TestCase TestCases[] = {
23622418
{"test_initconfig_api", test_initconfig_api},
23632419
{"test_initconfig_get_api", test_initconfig_get_api},
23642420
{"test_initconfig_exit", test_initconfig_exit},
2421+
{"test_initconfig_module", test_initconfig_module},
23652422
{"test_run_main", test_run_main},
23662423
{"test_run_main_loop", test_run_main_loop},
23672424
{"test_get_argc_argv", test_get_argc_argv},

Python/initconfig.c

+35
Original file line numberDiff line numberDiff line change
@@ -3424,6 +3424,8 @@ _Py_DumpPathConfig(PyThreadState *tstate)
34243424
struct PyInitConfig {
34253425
PyPreConfig preconfig;
34263426
PyConfig config;
3427+
struct _inittab *inittab;
3428+
Py_ssize_t inittab_size;
34273429
PyStatus status;
34283430
char *err_msg;
34293431
};
@@ -3867,9 +3869,42 @@ PyInitConfig_SetStrList(PyInitConfig *config, const char *name,
38673869
}
38683870

38693871

3872+
int
3873+
PyInitConfig_AddModule(PyInitConfig *config, const char *name,
3874+
PyObject* (*initfunc)(void))
3875+
{
3876+
size_t size = sizeof(struct _inittab) * (config->inittab_size + 2);
3877+
struct _inittab *new_inittab = PyMem_RawRealloc(config->inittab, size);
3878+
if (new_inittab == NULL) {
3879+
config->status = _PyStatus_NO_MEMORY();
3880+
return -1;
3881+
}
3882+
config->inittab = new_inittab;
3883+
3884+
struct _inittab *entry = &config->inittab[config->inittab_size];
3885+
entry->name = name;
3886+
entry->initfunc = initfunc;
3887+
3888+
// Terminator entry
3889+
entry = &config->inittab[config->inittab_size + 1];
3890+
entry->name = NULL;
3891+
entry->initfunc = NULL;
3892+
3893+
config->inittab_size++;
3894+
return 0;
3895+
}
3896+
3897+
38703898
int
38713899
Py_InitializeFromInitConfig(PyInitConfig *config)
38723900
{
3901+
if (config->inittab_size >= 1) {
3902+
if (PyImport_ExtendInittab(config->inittab) < 0) {
3903+
config->status = _PyStatus_NO_MEMORY();
3904+
return -1;
3905+
}
3906+
}
3907+
38733908
_PyPreConfig_GetConfig(&config->preconfig, &config->config);
38743909

38753910
config->status = Py_PreInitializeFromArgs(

0 commit comments

Comments
 (0)