diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index c7f45b59ac..033bdbbf03 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -51,7 +51,7 @@ class EmbeddingTests(unittest.TestCase): if p.returncode != 0 and support.verbose: print(f"--- {cmd} failed ---") print(f"stdout:\n{out}") - print(f"stderr:\n{out}") + print(f"stderr:\n{err}") print(f"------") self.assertEqual(p.returncode, 0, @@ -83,7 +83,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 @@ -96,7 +96,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) @@ -190,12 +190,23 @@ class EmbeddingTests(unittest.TestCase): def test_pre_initialization_api(self): """ - Checks the few parts of the C-API that work before the runtine + 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) - self.assertEqual(out, '') + # 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, '') def test_bpo20891(self): diff --git a/Programs/_testembed.c b/Programs/_testembed.c index b3d7aa442d..c7116d9b93 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -130,23 +130,53 @@ static int test_forced_io_encoding(void) * 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"); + Py_Initialize(); + _Py_EMBED_PREINIT_CHECK("Finalizing interpreter\n"); + Py_Finalize(); + + return 0; +} + + +/* bpo-20891: Avoid race condition when initialising the GIL */ static void bpo20891_thread(void *lockp) { PyThread_type_lock lock = *((PyThread_type_lock*)lockp); @@ -217,6 +247,7 @@ 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 }, { "bpo20891", test_bpo20891 }, { NULL, NULL } }; @@ -232,13 +263,13 @@ int main(int argc, char *argv[]) /* No match found, or no test name provided, so display usage */ printf("Python " PY_VERSION " _testembed executable for embedded interpreter tests\n" - "Normally executed via 'EmbeddingTests' in Lib/test/test_capi.py\n\n" + "Normally executed via 'EmbeddingTests' in Lib/test/test_embed.py\n\n" "Usage: %s TESTNAME\n\nAll available tests:\n", argv[0]); for (struct TestCase *tc = TestCases; tc && tc->name; tc++) { printf(" %s\n", tc->name); } - /* Non-zero exit code will cause test_capi.py tests to fail. + /* Non-zero exit code will cause test_embed.py tests to fail. This is intentional. */ return -1; }