mirror of
https://github.com/python/cpython.git
synced 2025-09-26 10:19:53 +00:00
bpo-44441: _PyImport_Fini2() resets PyImport_Inittab (GH-26874)
Py_RunMain() now resets PyImport_Inittab to its initial value at exit. It must be possible to call PyImport_AppendInittab() or PyImport_ExtendInittab() at each Python initialization.
This commit is contained in:
parent
019ad62afd
commit
489699ca05
6 changed files with 85 additions and 8 deletions
|
@ -299,4 +299,8 @@ Importing Modules
|
||||||
field; failure to provide the sentinel value can result in a memory fault.
|
field; failure to provide the sentinel value can result in a memory fault.
|
||||||
Returns ``0`` on success or ``-1`` if insufficient memory could be allocated to
|
Returns ``0`` on success or ``-1`` if insufficient memory could be allocated to
|
||||||
extend the internal table. In the event of failure, no modules are added to the
|
extend the internal table. In the event of failure, no modules are added to the
|
||||||
internal table. This should be called before :c:func:`Py_Initialize`.
|
internal table. This must be called before :c:func:`Py_Initialize`.
|
||||||
|
|
||||||
|
If Python is initialized multiple times, :c:func:`PyImport_AppendInittab` or
|
||||||
|
:c:func:`PyImport_ExtendInittab` must be called before each Python
|
||||||
|
initialization.
|
||||||
|
|
|
@ -1193,7 +1193,10 @@ The caller is responsible to handle exceptions (error or exit) using
|
||||||
|
|
||||||
If :c:func:`PyImport_FrozenModules`, :c:func:`PyImport_AppendInittab` or
|
If :c:func:`PyImport_FrozenModules`, :c:func:`PyImport_AppendInittab` or
|
||||||
:c:func:`PyImport_ExtendInittab` are used, they must be set or called after
|
:c:func:`PyImport_ExtendInittab` are used, they must be set or called after
|
||||||
Python preinitialization and before the Python initialization.
|
Python preinitialization and before the Python initialization. If Python is
|
||||||
|
initialized multiple times, :c:func:`PyImport_AppendInittab` or
|
||||||
|
:c:func:`PyImport_ExtendInittab` must be called before each Python
|
||||||
|
initialization.
|
||||||
|
|
||||||
The current configuration (``PyConfig`` type) is stored in
|
The current configuration (``PyConfig`` type) is stored in
|
||||||
``PyInterpreterState.config``.
|
``PyInterpreterState.config``.
|
||||||
|
|
|
@ -30,6 +30,7 @@ API_PYTHON = 2
|
||||||
# _PyCoreConfig_InitIsolatedConfig()
|
# _PyCoreConfig_InitIsolatedConfig()
|
||||||
API_ISOLATED = 3
|
API_ISOLATED = 3
|
||||||
|
|
||||||
|
INIT_LOOPS = 16
|
||||||
MAX_HASH_SEED = 4294967295
|
MAX_HASH_SEED = 4294967295
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,13 +112,13 @@ class EmbeddingTestsMixin:
|
||||||
self.assertEqual(err, "")
|
self.assertEqual(err, "")
|
||||||
|
|
||||||
# The output from _testembed looks like this:
|
# The output from _testembed looks like this:
|
||||||
# --- Pass 0 ---
|
# --- Pass 1 ---
|
||||||
# interp 0 <0x1cf9330>, thread state <0x1cf9700>: id(modules) = 139650431942728
|
# interp 0 <0x1cf9330>, thread state <0x1cf9700>: id(modules) = 139650431942728
|
||||||
# interp 1 <0x1d4f690>, thread state <0x1d35350>: id(modules) = 139650431165784
|
# interp 1 <0x1d4f690>, thread state <0x1d35350>: id(modules) = 139650431165784
|
||||||
# interp 2 <0x1d5a690>, thread state <0x1d99ed0>: id(modules) = 139650413140368
|
# interp 2 <0x1d5a690>, thread state <0x1d99ed0>: id(modules) = 139650413140368
|
||||||
# interp 3 <0x1d4f690>, thread state <0x1dc3340>: id(modules) = 139650412862200
|
# interp 3 <0x1d4f690>, thread state <0x1dc3340>: id(modules) = 139650412862200
|
||||||
# interp 0 <0x1cf9330>, thread state <0x1cf9700>: id(modules) = 139650431942728
|
# interp 0 <0x1cf9330>, thread state <0x1cf9700>: id(modules) = 139650431942728
|
||||||
# --- Pass 1 ---
|
# --- Pass 2 ---
|
||||||
# ...
|
# ...
|
||||||
|
|
||||||
interp_pat = (r"^interp (\d+) <(0x[\dA-F]+)>, "
|
interp_pat = (r"^interp (\d+) <(0x[\dA-F]+)>, "
|
||||||
|
@ -125,7 +126,7 @@ class EmbeddingTestsMixin:
|
||||||
r"id\(modules\) = ([\d]+)$")
|
r"id\(modules\) = ([\d]+)$")
|
||||||
Interp = namedtuple("Interp", "id interp tstate modules")
|
Interp = namedtuple("Interp", "id interp tstate modules")
|
||||||
|
|
||||||
numloops = 0
|
numloops = 1
|
||||||
current_run = []
|
current_run = []
|
||||||
for line in out.splitlines():
|
for line in out.splitlines():
|
||||||
if line == "--- Pass {} ---".format(numloops):
|
if line == "--- Pass {} ---".format(numloops):
|
||||||
|
@ -159,6 +160,8 @@ class EmbeddingTestsMixin:
|
||||||
|
|
||||||
|
|
||||||
class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
|
class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
|
maxDiff = 100 * 50
|
||||||
|
|
||||||
def test_subinterps_main(self):
|
def test_subinterps_main(self):
|
||||||
for run in self.run_repeated_init_and_subinterpreters():
|
for run in self.run_repeated_init_and_subinterpreters():
|
||||||
main = run[0]
|
main = run[0]
|
||||||
|
@ -194,6 +197,14 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||||
self.assertNotEqual(sub.tstate, main.tstate)
|
self.assertNotEqual(sub.tstate, main.tstate)
|
||||||
self.assertNotEqual(sub.modules, main.modules)
|
self.assertNotEqual(sub.modules, main.modules)
|
||||||
|
|
||||||
|
def test_repeated_init_and_inittab(self):
|
||||||
|
out, err = self.run_embedded_interpreter("test_repeated_init_and_inittab")
|
||||||
|
self.assertEqual(err, "")
|
||||||
|
|
||||||
|
lines = [f"--- Pass {i} ---" for i in range(1, INIT_LOOPS+1)]
|
||||||
|
lines = "\n".join(lines) + "\n"
|
||||||
|
self.assertEqual(out, lines)
|
||||||
|
|
||||||
def test_forced_io_encoding(self):
|
def test_forced_io_encoding(self):
|
||||||
# Checks forced configuration of embedded interpreter IO streams
|
# Checks forced configuration of embedded interpreter IO streams
|
||||||
env = dict(os.environ, PYTHONIOENCODING="utf-8:surrogateescape")
|
env = dict(os.environ, PYTHONIOENCODING="utf-8:surrogateescape")
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
:c:func:`Py_RunMain` now resets :c:data:`PyImport_Inittab` to its initial value
|
||||||
|
at exit. It must be possible to call :c:func:`PyImport_AppendInittab` or
|
||||||
|
:c:func:`PyImport_ExtendInittab` at each Python initialization.
|
||||||
|
Patch by Victor Stinner.
|
|
@ -22,6 +22,8 @@
|
||||||
/* Use path starting with "./" avoids a search along the PATH */
|
/* Use path starting with "./" avoids a search along the PATH */
|
||||||
#define PROGRAM_NAME L"./_testembed"
|
#define PROGRAM_NAME L"./_testembed"
|
||||||
|
|
||||||
|
#define INIT_LOOPS 16
|
||||||
|
|
||||||
// Ignore Py_DEPRECATED() compiler warnings: deprecated functions are
|
// Ignore Py_DEPRECATED() compiler warnings: deprecated functions are
|
||||||
// tested on purpose here.
|
// tested on purpose here.
|
||||||
_Py_COMP_DIAG_PUSH
|
_Py_COMP_DIAG_PUSH
|
||||||
|
@ -67,9 +69,8 @@ static int test_repeated_init_and_subinterpreters(void)
|
||||||
{
|
{
|
||||||
PyThreadState *mainstate, *substate;
|
PyThreadState *mainstate, *substate;
|
||||||
PyGILState_STATE gilstate;
|
PyGILState_STATE gilstate;
|
||||||
int i, j;
|
|
||||||
|
|
||||||
for (i=0; i<15; i++) {
|
for (int i=1; i <= INIT_LOOPS; i++) {
|
||||||
printf("--- Pass %d ---\n", i);
|
printf("--- Pass %d ---\n", i);
|
||||||
_testembed_Py_Initialize();
|
_testembed_Py_Initialize();
|
||||||
mainstate = PyThreadState_Get();
|
mainstate = PyThreadState_Get();
|
||||||
|
@ -80,7 +81,7 @@ static int test_repeated_init_and_subinterpreters(void)
|
||||||
print_subinterp();
|
print_subinterp();
|
||||||
PyThreadState_Swap(NULL);
|
PyThreadState_Swap(NULL);
|
||||||
|
|
||||||
for (j=0; j<3; j++) {
|
for (int j=0; j<3; j++) {
|
||||||
substate = Py_NewInterpreter();
|
substate = Py_NewInterpreter();
|
||||||
print_subinterp();
|
print_subinterp();
|
||||||
Py_EndInterpreter(substate);
|
Py_EndInterpreter(substate);
|
||||||
|
@ -96,6 +97,20 @@ static int test_repeated_init_and_subinterpreters(void)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define EMBEDDED_EXT_NAME "embedded_ext"
|
||||||
|
|
||||||
|
static PyModuleDef embedded_ext = {
|
||||||
|
PyModuleDef_HEAD_INIT,
|
||||||
|
.m_name = EMBEDDED_EXT_NAME,
|
||||||
|
.m_size = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
PyInit_embedded_ext(void)
|
||||||
|
{
|
||||||
|
return PyModule_Create(&embedded_ext);
|
||||||
|
}
|
||||||
|
|
||||||
/*****************************************************
|
/*****************************************************
|
||||||
* Test forcing a particular IO encoding
|
* Test forcing a particular IO encoding
|
||||||
*****************************************************/
|
*****************************************************/
|
||||||
|
@ -1790,6 +1805,38 @@ static int list_frozen(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int test_repeated_init_and_inittab(void)
|
||||||
|
{
|
||||||
|
// bpo-44441: Py_RunMain() must reset PyImport_Inittab at exit.
|
||||||
|
// It must be possible to call PyImport_AppendInittab() or
|
||||||
|
// PyImport_ExtendInittab() before each Python initialization.
|
||||||
|
for (int i=1; i <= INIT_LOOPS; i++) {
|
||||||
|
printf("--- Pass %d ---\n", i);
|
||||||
|
|
||||||
|
// Call PyImport_AppendInittab() at each iteration
|
||||||
|
if (PyImport_AppendInittab(EMBEDDED_EXT_NAME,
|
||||||
|
&PyInit_embedded_ext) != 0) {
|
||||||
|
fprintf(stderr, "PyImport_AppendInittab() failed\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize Python
|
||||||
|
wchar_t* argv[] = {PROGRAM_NAME, L"-c", L"pass"};
|
||||||
|
PyConfig config;
|
||||||
|
PyConfig_InitPythonConfig(&config);
|
||||||
|
config.isolated = 1;
|
||||||
|
config_set_argv(&config, Py_ARRAY_LENGTH(argv), argv);
|
||||||
|
init_from_config_clear(&config);
|
||||||
|
|
||||||
|
// Py_RunMain() calls _PyImport_Fini2() which resets PyImport_Inittab
|
||||||
|
int exitcode = Py_RunMain();
|
||||||
|
if (exitcode != 0) {
|
||||||
|
return exitcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* *********************************************************
|
/* *********************************************************
|
||||||
* List of test cases and the function that implements it.
|
* List of test cases and the function that implements it.
|
||||||
|
@ -1810,8 +1857,10 @@ struct TestCase
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct TestCase TestCases[] = {
|
static struct TestCase TestCases[] = {
|
||||||
|
// Python initialization
|
||||||
{"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_pre_initialization_api", test_pre_initialization_api},
|
{"test_pre_initialization_api", test_pre_initialization_api},
|
||||||
{"test_pre_initialization_sys_options", test_pre_initialization_sys_options},
|
{"test_pre_initialization_sys_options", test_pre_initialization_sys_options},
|
||||||
{"test_bpo20891", test_bpo20891},
|
{"test_bpo20891", test_bpo20891},
|
||||||
|
@ -1851,6 +1900,7 @@ static struct TestCase TestCases[] = {
|
||||||
{"test_run_main", test_run_main},
|
{"test_run_main", test_run_main},
|
||||||
{"test_get_argc_argv", test_get_argc_argv},
|
{"test_get_argc_argv", test_get_argc_argv},
|
||||||
|
|
||||||
|
// Audit
|
||||||
{"test_open_code_hook", test_open_code_hook},
|
{"test_open_code_hook", test_open_code_hook},
|
||||||
{"test_audit", test_audit},
|
{"test_audit", test_audit},
|
||||||
{"test_audit_subinterpreter", test_audit_subinterpreter},
|
{"test_audit_subinterpreter", test_audit_subinterpreter},
|
||||||
|
@ -1860,11 +1910,13 @@ static struct TestCase TestCases[] = {
|
||||||
{"test_audit_run_startup", test_audit_run_startup},
|
{"test_audit_run_startup", test_audit_run_startup},
|
||||||
{"test_audit_run_stdin", test_audit_run_stdin},
|
{"test_audit_run_stdin", test_audit_run_stdin},
|
||||||
|
|
||||||
|
// Specific C API
|
||||||
{"test_unicode_id_init", test_unicode_id_init},
|
{"test_unicode_id_init", test_unicode_id_init},
|
||||||
#ifndef MS_WINDOWS
|
#ifndef MS_WINDOWS
|
||||||
{"test_frozenmain", test_frozenmain},
|
{"test_frozenmain", test_frozenmain},
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Command
|
||||||
{"list_frozen", list_frozen},
|
{"list_frozen", list_frozen},
|
||||||
{NULL, NULL}
|
{NULL, NULL}
|
||||||
};
|
};
|
||||||
|
|
|
@ -255,6 +255,9 @@ _PyImport_Fini2(void)
|
||||||
PyMemAllocatorEx old_alloc;
|
PyMemAllocatorEx old_alloc;
|
||||||
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
_PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc);
|
||||||
|
|
||||||
|
// Reset PyImport_Inittab
|
||||||
|
PyImport_Inittab = _PyImport_Inittab;
|
||||||
|
|
||||||
/* Free memory allocated by PyImport_ExtendInittab() */
|
/* Free memory allocated by PyImport_ExtendInittab() */
|
||||||
PyMem_RawFree(inittab_copy);
|
PyMem_RawFree(inittab_copy);
|
||||||
inittab_copy = NULL;
|
inittab_copy = NULL;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue