22#include "pythread.h"
33#include <inttypes.h>
44#include <stdio.h>
5+ #include <wchar.h>
56
67/*********************************************************
78 * Embedded interpreter tests that need a custom exe
@@ -130,23 +131,89 @@ static int test_forced_io_encoding(void)
130131 * Test parts of the C-API that work before initialization
131132 *********************************************************/
132133
134+ /* The pre-initialization tests tend to break by segfaulting, so explicitly
135+ * flushed progress messages make the broken API easier to find when they fail.
136+ */
137+ #define _Py_EMBED_PREINIT_CHECK (msg ) \
138+ do {printf(msg); fflush(stdout);} while (0);
139+
133140static int test_pre_initialization_api (void )
134141{
135142 /* Leading "./" ensures getpath.c can still find the standard library */
143+ _Py_EMBED_PREINIT_CHECK ("Checking Py_DecodeLocale\n" );
136144 wchar_t * program = Py_DecodeLocale ("./spam" , NULL );
137145 if (program == NULL ) {
138146 fprintf (stderr , "Fatal error: cannot decode program name\n" );
139147 return 1 ;
140148 }
149+ _Py_EMBED_PREINIT_CHECK ("Checking Py_SetProgramName\n" );
141150 Py_SetProgramName (program );
142151
152+ _Py_EMBED_PREINIT_CHECK ("Initializing interpreter\n" );
143153 Py_Initialize ();
154+ _Py_EMBED_PREINIT_CHECK ("Check sys module contents\n" );
155+ PyRun_SimpleString ("import sys; "
156+ "print('sys.executable:', sys.executable)" );
157+ _Py_EMBED_PREINIT_CHECK ("Finalizing interpreter\n" );
144158 Py_Finalize ();
145159
160+ _Py_EMBED_PREINIT_CHECK ("Freeing memory allocated by Py_DecodeLocale\n" );
146161 PyMem_RawFree (program );
147162 return 0 ;
148163}
149164
165+
166+ /* bpo-33042: Ensure embedding apps can predefine sys module options */
167+ static int test_pre_initialization_sys_options (void )
168+ {
169+ /* We allocate a couple of the option dynamically, and then delete
170+ * them before calling Py_Initialize. This ensures the interpreter isn't
171+ * relying on the caller to keep the passed in strings alive.
172+ */
173+ wchar_t * static_warnoption = L"once" ;
174+ wchar_t * static_xoption = L"also_not_an_option=2" ;
175+ size_t warnoption_len = wcslen (static_warnoption );
176+ size_t xoption_len = wcslen (static_xoption );
177+ wchar_t * dynamic_once_warnoption = calloc (warnoption_len + 1 , sizeof (wchar_t ));
178+ wchar_t * dynamic_xoption = calloc (xoption_len + 1 , sizeof (wchar_t ));
179+ wcsncpy (dynamic_once_warnoption , static_warnoption , warnoption_len + 1 );
180+ wcsncpy (dynamic_xoption , static_xoption , xoption_len + 1 );
181+
182+ _Py_EMBED_PREINIT_CHECK ("Checking PySys_AddWarnOption\n" );
183+ PySys_AddWarnOption (L"default" );
184+ _Py_EMBED_PREINIT_CHECK ("Checking PySys_ResetWarnOptions\n" );
185+ PySys_ResetWarnOptions ();
186+ _Py_EMBED_PREINIT_CHECK ("Checking PySys_AddWarnOption linked list\n" );
187+ PySys_AddWarnOption (dynamic_once_warnoption );
188+ PySys_AddWarnOption (L"module" );
189+ PySys_AddWarnOption (L"default" );
190+ _Py_EMBED_PREINIT_CHECK ("Checking PySys_AddXOption\n" );
191+ PySys_AddXOption (L"not_an_option=1" );
192+ PySys_AddXOption (dynamic_xoption );
193+
194+ /* Delete the dynamic options early */
195+ free (dynamic_once_warnoption );
196+ dynamic_once_warnoption = NULL ;
197+ free (dynamic_xoption );
198+ dynamic_xoption = NULL ;
199+
200+ _Py_EMBED_PREINIT_CHECK ("Initializing interpreter\n" );
201+ _testembed_Py_Initialize ();
202+ _Py_EMBED_PREINIT_CHECK ("Check sys module contents\n" );
203+ PyRun_SimpleString ("import sys; "
204+ "print('sys.warnoptions:', sys.warnoptions); "
205+ "print('sys._xoptions:', sys._xoptions); "
206+ "warnings = sys.modules['warnings']; "
207+ "latest_filters = [f[0] for f in warnings.filters[:3]]; "
208+ "print('warnings.filters[:3]:', latest_filters)" );
209+ _Py_EMBED_PREINIT_CHECK ("Finalizing interpreter\n" );
210+ Py_Finalize ();
211+
212+ return 0 ;
213+ }
214+
215+
216+ /* bpo-20891: Avoid race condition when initialising the GIL */
150217static void bpo20891_thread (void * lockp )
151218{
152219 PyThread_type_lock lock = * ((PyThread_type_lock * )lockp );
@@ -217,6 +284,7 @@ static struct TestCase TestCases[] = {
217284 { "forced_io_encoding" , test_forced_io_encoding },
218285 { "repeated_init_and_subinterpreters" , test_repeated_init_and_subinterpreters },
219286 { "pre_initialization_api" , test_pre_initialization_api },
287+ { "pre_initialization_sys_options" , test_pre_initialization_sys_options },
220288 { "bpo20891" , test_bpo20891 },
221289 { NULL , NULL }
222290};
@@ -232,13 +300,13 @@ int main(int argc, char *argv[])
232300
233301 /* No match found, or no test name provided, so display usage */
234302 printf ("Python " PY_VERSION " _testembed executable for embedded interpreter tests\n"
235- "Normally executed via 'EmbeddingTests' in Lib/test/test_capi .py\n\n"
303+ "Normally executed via 'EmbeddingTests' in Lib/test/test_embed .py\n\n"
236304 "Usage: %s TESTNAME\n\nAll available tests:\n" , argv [0 ]);
237305 for (struct TestCase * tc = TestCases ; tc && tc -> name ; tc ++ ) {
238306 printf (" %s\n" , tc -> name );
239307 }
240308
241- /* Non-zero exit code will cause test_capi .py tests to fail.
309+ /* Non-zero exit code will cause test_embed .py tests to fail.
242310 This is intentional. */
243311 return -1 ;
244312}
0 commit comments