diff --git a/Lib/test/test_capi.py b/Lib/test/test_capi.py index c3a04b49df..c6b2ee1617 100644 --- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -448,7 +448,7 @@ class EmbeddingTests(unittest.TestCase): for line in out.splitlines(): if line == "--- Pass {} ---".format(numloops): self.assertEqual(len(current_run), 0) - if support.verbose: + if support.verbose > 1: print(line) numloops += 1 continue @@ -461,7 +461,7 @@ class EmbeddingTests(unittest.TestCase): # Parse the line from the loop. The first line is the main # interpreter and the 3 afterward are subinterpreters. interp = Interp(*match.groups()) - if support.verbose: + if support.verbose > 1: print(interp) self.assertTrue(interp.interp) self.assertTrue(interp.tstate) @@ -553,6 +553,27 @@ class EmbeddingTests(unittest.TestCase): self.maxDiff = None self.assertEqual(out.strip(), expected_output) + def test_pre_initialization_api(self): + """ + Checks some key parts of the C-API that need to work before the runtine + is initialized (via Py_Initialize()). + """ + env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path)) + out, err = self.run_embedded_interpreter("pre_initialization_api", env=env) + # Allow debugging output on "out" + self.assertEqual(err, '') + + def test_pre_initialization_sys_options(self): + """ + Checks that sys.warnoptions and sys._xoptions can be set before the + runtime is initialized (otherwise they won't be effective). + """ + env = dict(os.environ, PYTHONPATH=os.pathsep.join(sys.path)) + out, err = self.run_embedded_interpreter( + "pre_initialization_sys_options", env=env) + # Allow debugging output on "out" + self.assertEqual(err, '') + class SkipitemTest(unittest.TestCase): diff --git a/Programs/_testembed.c b/Programs/_testembed.c index c7660f9581..d2a4622c7f 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -131,9 +131,59 @@ static int test_forced_io_encoding(void) return 0; } +/********************************************************** + * Test parts of the C-API that work before initialization + *********************************************************/ + +/* The pre-initialization tests tend to break by segfaulting, so explicitly + * flushed progress messages make the broken API easier to find when they fail. + */ +#define _Py_EMBED_PREINIT_CHECK(msg) \ + do {printf(msg); fflush(stdout);} while (0); + +static int test_pre_initialization_api(void) +{ + /* Leading "./" ensures getpath.c can still find the standard library */ + _Py_EMBED_PREINIT_CHECK("Checking Py_DecodeLocale\n"); + wchar_t *program = Py_DecodeLocale("./spam", NULL); + if (program == NULL) { + fprintf(stderr, "Fatal error: cannot decode program name\n"); + return 1; + } + _Py_EMBED_PREINIT_CHECK("Checking Py_SetProgramName\n"); + Py_SetProgramName(program); + + _Py_EMBED_PREINIT_CHECK("Initializing interpreter\n"); + Py_Initialize(); + _Py_EMBED_PREINIT_CHECK("Finalizing interpreter\n"); + Py_Finalize(); + + _Py_EMBED_PREINIT_CHECK("Freeing memory allocated by Py_DecodeLocale\n"); + PyMem_RawFree(program); + return 0; +} + + +/* bpo-33042: Ensure embedding apps can predefine sys module options */ +static int test_pre_initialization_sys_options(void) +{ + _Py_EMBED_PREINIT_CHECK("Checking Py_SysAddWarnOption\n"); + PySys_AddWarnOption(L"default"); + _Py_EMBED_PREINIT_CHECK("Checking Py_SysAddXOption\n"); + PySys_AddXOption(L"not_an_option=1"); + + _Py_EMBED_PREINIT_CHECK("Initializing interpreter\n"); + _testembed_Py_Initialize(); + _Py_EMBED_PREINIT_CHECK("Finalizing interpreter\n"); + Py_Finalize(); + + return 0; +} + + /* ********************************************************* * List of test cases and the function that implements it. - * + * * Names are compared case-sensitively with the first * argument. If no match is found, or no first argument was * provided, the names of all test cases are printed and @@ -141,7 +191,7 @@ static int test_forced_io_encoding(void) * * The int returned from test functions is used as the exit * code, and test_capi treats all non-zero exit codes as a - * failed test. + * failed test. *********************************************************/ struct TestCase { @@ -152,6 +202,8 @@ struct TestCase static struct TestCase TestCases[] = { { "forced_io_encoding", test_forced_io_encoding }, { "repeated_init_and_subinterpreters", test_repeated_init_and_subinterpreters }, + { "pre_initialization_api", test_pre_initialization_api }, + { "pre_initialization_sys_options", test_pre_initialization_sys_options }, { NULL, NULL } };