2222/* Use path starting with "./" avoids a search along the PATH */
2323#define PROGRAM_NAME L"./_testembed"
2424
25+ #define INIT_LOOPS 16
26+
27+
2528static void _testembed_Py_Initialize (void )
2629{
2730 Py_SetProgramName (PROGRAM_NAME );
@@ -54,9 +57,8 @@ static int test_repeated_init_and_subinterpreters(void)
5457{
5558 PyThreadState * mainstate , * substate ;
5659 PyGILState_STATE gilstate ;
57- int i , j ;
5860
59- for (i = 0 ; i < 15 ; i ++ ) {
61+ for (int i = 1 ; i <= INIT_LOOPS ; i ++ ) {
6062 printf ("--- Pass %d ---\n" , i );
6163 _testembed_Py_Initialize ();
6264 mainstate = PyThreadState_Get ();
@@ -67,7 +69,7 @@ static int test_repeated_init_and_subinterpreters(void)
6769 print_subinterp ();
6870 PyThreadState_Swap (NULL );
6971
70- for (j = 0 ; j < 3 ; j ++ ) {
72+ for (int j = 0 ; j < 3 ; j ++ ) {
7173 substate = Py_NewInterpreter ();
7274 print_subinterp ();
7375 Py_EndInterpreter (substate );
@@ -83,6 +85,20 @@ static int test_repeated_init_and_subinterpreters(void)
8385 return 0 ;
8486}
8587
88+ #define EMBEDDED_EXT_NAME "embedded_ext"
89+
90+ static PyModuleDef embedded_ext = {
91+ PyModuleDef_HEAD_INIT ,
92+ .m_name = EMBEDDED_EXT_NAME ,
93+ .m_size = 0 ,
94+ };
95+
96+ static PyObject *
97+ PyInit_embedded_ext (void )
98+ {
99+ return PyModule_Create (& embedded_ext );
100+ }
101+
86102/*****************************************************
87103 * Test forcing a particular IO encoding
88104 *****************************************************/
@@ -1641,6 +1657,39 @@ static int test_get_argc_argv(void)
16411657}
16421658
16431659
1660+ static int test_repeated_init_and_inittab (void )
1661+ {
1662+ // bpo-44441: Py_RunMain() must reset PyImport_Inittab at exit.
1663+ // It must be possible to call PyImport_AppendInittab() or
1664+ // PyImport_ExtendInittab() before each Python initialization.
1665+ for (int i = 1 ; i <= INIT_LOOPS ; i ++ ) {
1666+ printf ("--- Pass %d ---\n" , i );
1667+
1668+ // Call PyImport_AppendInittab() at each iteration
1669+ if (PyImport_AppendInittab (EMBEDDED_EXT_NAME ,
1670+ & PyInit_embedded_ext ) != 0 ) {
1671+ fprintf (stderr , "PyImport_AppendInittab() failed\n" );
1672+ return 1 ;
1673+ }
1674+
1675+ // Initialize Python
1676+ wchar_t * argv [] = {PROGRAM_NAME , L"-c" , L"pass" };
1677+ PyConfig config ;
1678+ PyConfig_InitPythonConfig (& config );
1679+ config .isolated = 1 ;
1680+ config_set_argv (& config , Py_ARRAY_LENGTH (argv ), argv );
1681+ init_from_config_clear (& config );
1682+
1683+ // Py_RunMain() calls _PyImport_Fini2() which resets PyImport_Inittab
1684+ int exitcode = Py_RunMain ();
1685+ if (exitcode != 0 ) {
1686+ return exitcode ;
1687+ }
1688+ }
1689+ return 0 ;
1690+ }
1691+
1692+
16441693/* *********************************************************
16451694 * List of test cases and the function that implements it.
16461695 *
@@ -1660,8 +1709,10 @@ struct TestCase
16601709};
16611710
16621711static struct TestCase TestCases [] = {
1712+ // Python initialization
16631713 {"test_forced_io_encoding" , test_forced_io_encoding },
16641714 {"test_repeated_init_and_subinterpreters" , test_repeated_init_and_subinterpreters },
1715+ {"test_repeated_init_and_inittab" , test_repeated_init_and_inittab },
16651716 {"test_pre_initialization_api" , test_pre_initialization_api },
16661717 {"test_pre_initialization_sys_options" , test_pre_initialization_sys_options },
16671718 {"test_bpo20891" , test_bpo20891 },
@@ -1700,6 +1751,7 @@ static struct TestCase TestCases[] = {
17001751 {"test_run_main" , test_run_main },
17011752 {"test_get_argc_argv" , test_get_argc_argv },
17021753
1754+ // Audit
17031755 {"test_open_code_hook" , test_open_code_hook },
17041756 {"test_audit" , test_audit },
17051757 {"test_audit_subinterpreter" , test_audit_subinterpreter },
0 commit comments