Skip to content

Commit 1ca74e2

Browse files
committed
pythongh-107954, PEP 741: Add PyInitConfig_AddModule() function
1 parent c08ede2 commit 1ca74e2

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
@@ -1775,6 +1775,9 @@ def test_initconfig_get_api(self):
17751775
def test_initconfig_exit(self):
17761776
self.run_embedded_interpreter("test_initconfig_exit")
17771777

1778+
def test_initconfig_module(self):
1779+
self.run_embedded_interpreter("test_initconfig_module")
1780+
17781781
def test_get_argc_argv(self):
17791782
self.run_embedded_interpreter("test_get_argc_argv")
17801783
# 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
@@ -1963,6 +1963,62 @@ static int test_initconfig_exit(void)
19631963
}
19641964

19651965

1966+
static PyModuleDef_Slot extension_slots[] = {
1967+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
1968+
{0, NULL}
1969+
};
1970+
1971+
static struct PyModuleDef extension_module = {
1972+
PyModuleDef_HEAD_INIT,
1973+
.m_name = "my_test_extension",
1974+
.m_size = 0,
1975+
.m_slots = extension_slots,
1976+
};
1977+
1978+
static PyObject* init_my_test_extension(void)
1979+
{
1980+
return PyModuleDef_Init(&extension_module);
1981+
}
1982+
1983+
1984+
static int test_initconfig_module(void)
1985+
{
1986+
PyInitConfig *config = PyInitConfig_Create();
1987+
if (config == NULL) {
1988+
printf("Init allocation error\n");
1989+
return 1;
1990+
}
1991+
1992+
if (PyInitConfig_SetStr(config, "program_name", PROGRAM_NAME_UTF8) < 0) {
1993+
goto error;
1994+
}
1995+
1996+
if (PyInitConfig_AddModule(config, "my_test_extension",
1997+
init_my_test_extension) < 0) {
1998+
goto error;
1999+
}
2000+
2001+
if (Py_InitializeFromInitConfig(config) < 0) {
2002+
goto error;
2003+
}
2004+
PyInitConfig_Free(config);
2005+
2006+
if (PyRun_SimpleString("import my_test_extension") < 0) {
2007+
fprintf(stderr, "unable to import my_test_extension\n");
2008+
exit(1);
2009+
}
2010+
2011+
Py_Finalize();
2012+
return 0;
2013+
2014+
const char *err_msg;
2015+
error:
2016+
(void)PyInitConfig_GetError(config, &err_msg);
2017+
printf("Python init failed: %s\n", err_msg);
2018+
exit(1);
2019+
}
2020+
2021+
19662022
static void configure_init_main(PyConfig *config)
19672023
{
19682024
wchar_t* argv[] = {
@@ -2384,6 +2440,7 @@ static struct TestCase TestCases[] = {
23842440
{"test_initconfig_api", test_initconfig_api},
23852441
{"test_initconfig_get_api", test_initconfig_get_api},
23862442
{"test_initconfig_exit", test_initconfig_exit},
2443+
{"test_initconfig_module", test_initconfig_module},
23872444
{"test_run_main", test_run_main},
23882445
{"test_run_main_loop", test_run_main_loop},
23892446
{"test_get_argc_argv", test_get_argc_argv},

Python/initconfig.c

+35
Original file line numberDiff line numberDiff line change
@@ -3423,6 +3423,8 @@ _Py_DumpPathConfig(PyThreadState *tstate)
34233423
struct PyInitConfig {
34243424
PyPreConfig preconfig;
34253425
PyConfig config;
3426+
struct _inittab *inittab;
3427+
Py_ssize_t inittab_size;
34263428
PyStatus status;
34273429
char *err_msg;
34283430
};
@@ -3873,9 +3875,42 @@ PyInitConfig_SetStrList(PyInitConfig *config, const char *name,
38733875
}
38743876

38753877

3878+
int
3879+
PyInitConfig_AddModule(PyInitConfig *config, const char *name,
3880+
PyObject* (*initfunc)(void))
3881+
{
3882+
size_t size = sizeof(struct _inittab) * (config->inittab_size + 2);
3883+
struct _inittab *new_inittab = PyMem_RawRealloc(config->inittab, size);
3884+
if (new_inittab == NULL) {
3885+
config->status = _PyStatus_NO_MEMORY();
3886+
return -1;
3887+
}
3888+
config->inittab = new_inittab;
3889+
3890+
struct _inittab *entry = &config->inittab[config->inittab_size];
3891+
entry->name = name;
3892+
entry->initfunc = initfunc;
3893+
3894+
// Terminator entry
3895+
entry = &config->inittab[config->inittab_size + 1];
3896+
entry->name = NULL;
3897+
entry->initfunc = NULL;
3898+
3899+
config->inittab_size++;
3900+
return 0;
3901+
}
3902+
3903+
38763904
int
38773905
Py_InitializeFromInitConfig(PyInitConfig *config)
38783906
{
3907+
if (config->inittab_size >= 1) {
3908+
if (PyImport_ExtendInittab(config->inittab) < 0) {
3909+
config->status = _PyStatus_NO_MEMORY();
3910+
return -1;
3911+
}
3912+
}
3913+
38793914
_PyPreConfig_GetConfig(&config->preconfig, &config->config);
38803915

38813916
config->status = Py_PreInitializeFromArgs(

0 commit comments

Comments
 (0)