mirror of
https://github.com/python/cpython.git
synced 2025-08-31 14:07:50 +00:00
gh-96853: Restore test coverage for Py_Initialize(Ex) (GH-98212)
* As most of `test_embed` now uses `Py_InitializeFromConfig`, add a specific test case to cover `Py_Initialize` (and `Py_InitializeEx`) * Rename `_testembed` init helper to clarify the API used * Add a `PyConfig_Clear` call in `Py_InitializeEx` to make the code more obviously correct (it already didn't leak as none of the dynamically allocated config fields were being populated, but it's clearer if the wrappers follow the documented API usage guidelines)
This commit is contained in:
parent
76f989dc3e
commit
05e48865be
5 changed files with 57 additions and 19 deletions
|
@ -340,6 +340,12 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
out, err = self.run_embedded_interpreter("test_repeated_init_exec", code)
|
out, err = self.run_embedded_interpreter("test_repeated_init_exec", code)
|
||||||
self.assertEqual(out, 'Tests passed\n' * INIT_LOOPS)
|
self.assertEqual(out, 'Tests passed\n' * INIT_LOOPS)
|
||||||
|
|
||||||
|
def test_simple_initialization_api(self):
|
||||||
|
# _testembed now uses Py_InitializeFromConfig by default
|
||||||
|
# This case specifically checks Py_Initialize(Ex) still works
|
||||||
|
out, err = self.run_embedded_interpreter("test_repeated_simple_init")
|
||||||
|
self.assertEqual(out, 'Finalized\n' * INIT_LOOPS)
|
||||||
|
|
||||||
def test_quickened_static_code_gets_unquickened_at_Py_FINALIZE(self):
|
def test_quickened_static_code_gets_unquickened_at_Py_FINALIZE(self):
|
||||||
# https://github.com/python/cpython/issues/92031
|
# https://github.com/python/cpython/issues/92031
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
``Py_InitializeEx`` now correctly calls ``PyConfig_Clear`` after initializing
|
||||||
|
the interpreter (the omission didn't cause a memory leak only because none
|
||||||
|
of the dynamically allocated config fields are populated by the wrapper
|
||||||
|
function)
|
|
@ -0,0 +1,3 @@
|
||||||
|
Added explicit coverage of ``Py_Initialize`` (and hence ``Py_InitializeEx``)
|
||||||
|
back to the embedding tests (all other embedding tests migrated to
|
||||||
|
``Py_InitializeFromConfig`` in Python 3.11)
|
|
@ -22,7 +22,7 @@ char **main_argv;
|
||||||
/*********************************************************
|
/*********************************************************
|
||||||
* Embedded interpreter tests that need a custom exe
|
* Embedded interpreter tests that need a custom exe
|
||||||
*
|
*
|
||||||
* Executed via 'EmbeddingTests' in Lib/test/test_capi.py
|
* Executed via Lib/test/test_embed.py
|
||||||
*********************************************************/
|
*********************************************************/
|
||||||
|
|
||||||
// Use to display the usage
|
// Use to display the usage
|
||||||
|
@ -73,7 +73,7 @@ static void init_from_config_clear(PyConfig *config)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void _testembed_Py_Initialize(void)
|
static void _testembed_Py_InitializeFromConfig(void)
|
||||||
{
|
{
|
||||||
PyConfig config;
|
PyConfig config;
|
||||||
_PyConfig_InitCompatConfig(&config);
|
_PyConfig_InitCompatConfig(&config);
|
||||||
|
@ -81,6 +81,12 @@ static void _testembed_Py_Initialize(void)
|
||||||
init_from_config_clear(&config);
|
init_from_config_clear(&config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _testembed_Py_Initialize(void)
|
||||||
|
{
|
||||||
|
Py_SetProgramName(PROGRAM_NAME);
|
||||||
|
Py_Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************
|
/*****************************************************
|
||||||
* Test repeated initialisation and subinterpreters
|
* Test repeated initialisation and subinterpreters
|
||||||
|
@ -110,7 +116,7 @@ static int test_repeated_init_and_subinterpreters(void)
|
||||||
|
|
||||||
for (int i=1; i <= INIT_LOOPS; i++) {
|
for (int i=1; i <= INIT_LOOPS; i++) {
|
||||||
printf("--- Pass %d ---\n", i);
|
printf("--- Pass %d ---\n", i);
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
mainstate = PyThreadState_Get();
|
mainstate = PyThreadState_Get();
|
||||||
|
|
||||||
PyEval_ReleaseThread(mainstate);
|
PyEval_ReleaseThread(mainstate);
|
||||||
|
@ -168,7 +174,7 @@ static int test_repeated_init_exec(void)
|
||||||
fprintf(stderr, "--- Loop #%d ---\n", i);
|
fprintf(stderr, "--- Loop #%d ---\n", i);
|
||||||
fflush(stderr);
|
fflush(stderr);
|
||||||
|
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
int err = PyRun_SimpleString(code);
|
int err = PyRun_SimpleString(code);
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
if (err) {
|
if (err) {
|
||||||
|
@ -178,6 +184,23 @@ static int test_repeated_init_exec(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/****************************************************************************
|
||||||
|
* Test the Py_Initialize(Ex) convenience/compatibility wrappers
|
||||||
|
***************************************************************************/
|
||||||
|
// This is here to help ensure there are no wrapper resource leaks (gh-96853)
|
||||||
|
static int test_repeated_simple_init(void)
|
||||||
|
{
|
||||||
|
for (int i=1; i <= INIT_LOOPS; i++) {
|
||||||
|
fprintf(stderr, "--- Loop #%d ---\n", i);
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
|
_testembed_Py_Initialize();
|
||||||
|
Py_Finalize();
|
||||||
|
printf("Finalized\n"); // Give test_embed some output to check
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************
|
/*****************************************************
|
||||||
* Test forcing a particular IO encoding
|
* Test forcing a particular IO encoding
|
||||||
|
@ -199,7 +222,7 @@ static void check_stdio_details(const char *encoding, const char * errors)
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
/* Force the given IO encoding */
|
/* Force the given IO encoding */
|
||||||
Py_SetStandardStreamEncoding(encoding, errors);
|
Py_SetStandardStreamEncoding(encoding, errors);
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
PyRun_SimpleString(
|
PyRun_SimpleString(
|
||||||
"import sys;"
|
"import sys;"
|
||||||
"print('stdin: {0.encoding}:{0.errors}'.format(sys.stdin));"
|
"print('stdin: {0.encoding}:{0.errors}'.format(sys.stdin));"
|
||||||
|
@ -308,7 +331,7 @@ static int test_pre_initialization_sys_options(void)
|
||||||
dynamic_xoption = NULL;
|
dynamic_xoption = NULL;
|
||||||
|
|
||||||
_Py_EMBED_PREINIT_CHECK("Initializing interpreter\n");
|
_Py_EMBED_PREINIT_CHECK("Initializing interpreter\n");
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
_Py_EMBED_PREINIT_CHECK("Check sys module contents\n");
|
_Py_EMBED_PREINIT_CHECK("Check sys module contents\n");
|
||||||
PyRun_SimpleString("import sys; "
|
PyRun_SimpleString("import sys; "
|
||||||
"print('sys.warnoptions:', sys.warnoptions); "
|
"print('sys.warnoptions:', sys.warnoptions); "
|
||||||
|
@ -352,7 +375,7 @@ static int test_bpo20891(void)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
|
|
||||||
unsigned long thrd = PyThread_start_new_thread(bpo20891_thread, &lock);
|
unsigned long thrd = PyThread_start_new_thread(bpo20891_thread, &lock);
|
||||||
if (thrd == PYTHREAD_INVALID_THREAD_ID) {
|
if (thrd == PYTHREAD_INVALID_THREAD_ID) {
|
||||||
|
@ -375,7 +398,7 @@ static int test_bpo20891(void)
|
||||||
|
|
||||||
static int test_initialize_twice(void)
|
static int test_initialize_twice(void)
|
||||||
{
|
{
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
|
|
||||||
/* bpo-33932: Calling Py_Initialize() twice should do nothing
|
/* bpo-33932: Calling Py_Initialize() twice should do nothing
|
||||||
* (and not crash!). */
|
* (and not crash!). */
|
||||||
|
@ -393,7 +416,7 @@ static int test_initialize_pymain(void)
|
||||||
L"print(f'Py_Main() after Py_Initialize: "
|
L"print(f'Py_Main() after Py_Initialize: "
|
||||||
L"sys.argv={sys.argv}')"),
|
L"sys.argv={sys.argv}')"),
|
||||||
L"arg2"};
|
L"arg2"};
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
|
|
||||||
/* bpo-34008: Calling Py_Main() after Py_Initialize() must not crash */
|
/* bpo-34008: Calling Py_Main() after Py_Initialize() must not crash */
|
||||||
Py_Main(Py_ARRAY_LENGTH(argv), argv);
|
Py_Main(Py_ARRAY_LENGTH(argv), argv);
|
||||||
|
@ -416,7 +439,7 @@ dump_config(void)
|
||||||
|
|
||||||
static int test_init_initialize_config(void)
|
static int test_init_initialize_config(void)
|
||||||
{
|
{
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
dump_config();
|
dump_config();
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -767,7 +790,7 @@ static int test_init_compat_env(void)
|
||||||
/* Test initialization from environment variables */
|
/* Test initialization from environment variables */
|
||||||
Py_IgnoreEnvironmentFlag = 0;
|
Py_IgnoreEnvironmentFlag = 0;
|
||||||
set_all_env_vars();
|
set_all_env_vars();
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
dump_config();
|
dump_config();
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -803,7 +826,7 @@ static int test_init_env_dev_mode(void)
|
||||||
/* Test initialization from environment variables */
|
/* Test initialization from environment variables */
|
||||||
Py_IgnoreEnvironmentFlag = 0;
|
Py_IgnoreEnvironmentFlag = 0;
|
||||||
set_all_env_vars_dev_mode();
|
set_all_env_vars_dev_mode();
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
dump_config();
|
dump_config();
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -816,7 +839,7 @@ static int test_init_env_dev_mode_alloc(void)
|
||||||
Py_IgnoreEnvironmentFlag = 0;
|
Py_IgnoreEnvironmentFlag = 0;
|
||||||
set_all_env_vars_dev_mode();
|
set_all_env_vars_dev_mode();
|
||||||
putenv("PYTHONMALLOC=malloc");
|
putenv("PYTHONMALLOC=malloc");
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
dump_config();
|
dump_config();
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1156,7 +1179,7 @@ static int test_open_code_hook(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_IgnoreEnvironmentFlag = 0;
|
Py_IgnoreEnvironmentFlag = 0;
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
result = 0;
|
result = 0;
|
||||||
|
|
||||||
PyObject *r = PyFile_OpenCode("$$test-filename");
|
PyObject *r = PyFile_OpenCode("$$test-filename");
|
||||||
|
@ -1220,7 +1243,7 @@ static int _test_audit(Py_ssize_t setValue)
|
||||||
|
|
||||||
Py_IgnoreEnvironmentFlag = 0;
|
Py_IgnoreEnvironmentFlag = 0;
|
||||||
PySys_AddAuditHook(_audit_hook, &sawSet);
|
PySys_AddAuditHook(_audit_hook, &sawSet);
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
|
|
||||||
if (PySys_Audit("_testembed.raise", NULL) == 0) {
|
if (PySys_Audit("_testembed.raise", NULL) == 0) {
|
||||||
printf("No error raised");
|
printf("No error raised");
|
||||||
|
@ -1276,7 +1299,7 @@ static int test_audit_subinterpreter(void)
|
||||||
{
|
{
|
||||||
Py_IgnoreEnvironmentFlag = 0;
|
Py_IgnoreEnvironmentFlag = 0;
|
||||||
PySys_AddAuditHook(_audit_subinterpreter_hook, NULL);
|
PySys_AddAuditHook(_audit_subinterpreter_hook, NULL);
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
|
|
||||||
Py_NewInterpreter();
|
Py_NewInterpreter();
|
||||||
Py_NewInterpreter();
|
Py_NewInterpreter();
|
||||||
|
@ -1871,13 +1894,13 @@ static int test_unicode_id_init(void)
|
||||||
_Py_IDENTIFIER(test_unicode_id_init);
|
_Py_IDENTIFIER(test_unicode_id_init);
|
||||||
|
|
||||||
// Initialize Python once without using the identifier
|
// Initialize Python once without using the identifier
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
Py_Finalize();
|
Py_Finalize();
|
||||||
|
|
||||||
// Now initialize Python multiple times and use the identifier.
|
// Now initialize Python multiple times and use the identifier.
|
||||||
// The first _PyUnicode_FromId() call initializes the identifier index.
|
// The first _PyUnicode_FromId() call initializes the identifier index.
|
||||||
for (int i=0; i<3; i++) {
|
for (int i=0; i<3; i++) {
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
|
|
||||||
PyObject *str1, *str2;
|
PyObject *str1, *str2;
|
||||||
|
|
||||||
|
@ -2021,7 +2044,7 @@ unwrap_allocator(PyMemAllocatorEx *allocator)
|
||||||
static int
|
static int
|
||||||
test_get_incomplete_frame(void)
|
test_get_incomplete_frame(void)
|
||||||
{
|
{
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_InitializeFromConfig();
|
||||||
PyMemAllocatorEx allocator;
|
PyMemAllocatorEx allocator;
|
||||||
wrap_allocator(&allocator);
|
wrap_allocator(&allocator);
|
||||||
// Force an allocation with an incomplete (generator) frame:
|
// Force an allocation with an incomplete (generator) frame:
|
||||||
|
@ -2053,6 +2076,7 @@ struct TestCase
|
||||||
static struct TestCase TestCases[] = {
|
static struct TestCase TestCases[] = {
|
||||||
// Python initialization
|
// Python initialization
|
||||||
{"test_repeated_init_exec", test_repeated_init_exec},
|
{"test_repeated_init_exec", test_repeated_init_exec},
|
||||||
|
{"test_repeated_simple_init", test_repeated_simple_init},
|
||||||
{"test_forced_io_encoding", test_forced_io_encoding},
|
{"test_forced_io_encoding", test_forced_io_encoding},
|
||||||
{"test_repeated_init_and_subinterpreters", test_repeated_init_and_subinterpreters},
|
{"test_repeated_init_and_subinterpreters", test_repeated_init_and_subinterpreters},
|
||||||
{"test_repeated_init_and_inittab", test_repeated_init_and_inittab},
|
{"test_repeated_init_and_inittab", test_repeated_init_and_inittab},
|
||||||
|
|
|
@ -1313,6 +1313,7 @@ Py_InitializeEx(int install_sigs)
|
||||||
config.install_signal_handlers = install_sigs;
|
config.install_signal_handlers = install_sigs;
|
||||||
|
|
||||||
status = Py_InitializeFromConfig(&config);
|
status = Py_InitializeFromConfig(&config);
|
||||||
|
PyConfig_Clear(&config);
|
||||||
if (_PyStatus_EXCEPTION(status)) {
|
if (_PyStatus_EXCEPTION(status)) {
|
||||||
Py_ExitStatusException(status);
|
Py_ExitStatusException(status);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue