bpo-35233: Fix _PyMainInterpreterConfig_Copy() (GH-10519)
* Fix _PyMainInterpreterConfig_Copy(): copy 'install_signal_handlers' attribute * Add _PyMainInterpreterConfig_AsDict() * Add unit tests on the main interpreter configuration to test_embed.InitConfigTests * test.pythoninfo: log also main_config
This commit is contained in:
parent
f966e5397e
commit
00b137c72f
|
@ -37,6 +37,8 @@ PyAPI_FUNC(void) _PyMainInterpreterConfig_Clear(_PyMainInterpreterConfig *);
|
|||
PyAPI_FUNC(int) _PyMainInterpreterConfig_Copy(
|
||||
_PyMainInterpreterConfig *config,
|
||||
const _PyMainInterpreterConfig *config2);
|
||||
PyAPI_FUNC(PyObject*) _PyMainInterpreterConfig_AsDict(
|
||||
const _PyMainInterpreterConfig *config);
|
||||
|
||||
PyAPI_FUNC(_PyInitError) _Py_InitializeMainInterpreter(
|
||||
PyInterpreterState *interp,
|
||||
|
|
|
@ -534,15 +534,25 @@ def collect_gdbm(info_add):
|
|||
info_add('gdbm.GDBM_VERSION', '.'.join(map(str, _GDBM_VERSION)))
|
||||
|
||||
|
||||
def collect_get_coreconfig(info_add):
|
||||
def collect_get_config(info_add):
|
||||
# Dump _PyCoreConfig and _PyMainInterpreterConfig
|
||||
try:
|
||||
from _testcapi import get_coreconfig
|
||||
except ImportError:
|
||||
return
|
||||
pass
|
||||
else:
|
||||
config = get_coreconfig()
|
||||
for key in sorted(config):
|
||||
info_add('core_config[%s]' % key, repr(config[key]))
|
||||
|
||||
config = get_coreconfig()
|
||||
for key in sorted(config):
|
||||
info_add('coreconfig[%s]' % key, repr(config[key]))
|
||||
try:
|
||||
from _testcapi import get_mainconfig
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
config = get_mainconfig()
|
||||
for key in sorted(config):
|
||||
info_add('main_config[%s]' % key, repr(config[key]))
|
||||
|
||||
|
||||
def collect_info(info):
|
||||
|
@ -573,7 +583,7 @@ def collect_info(info):
|
|||
collect_resource,
|
||||
collect_cc,
|
||||
collect_gdbm,
|
||||
collect_get_coreconfig,
|
||||
collect_get_config,
|
||||
|
||||
# Collecting from tests should be last as they have side effects.
|
||||
collect_test_socket,
|
||||
|
|
|
@ -255,9 +255,11 @@ class EmbeddingTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
|
||||
class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
||||
maxDiff = 4096
|
||||
CORE_CONFIG_REGEX = re.compile(r"^core_config\[([^]]*)\] = (.*)$")
|
||||
MAIN_CONFIG_REGEX = re.compile(r"^main_config\[([^]]*)\] = (.*)$")
|
||||
UTF8_MODE_ERRORS = ('surrogatepass' if sys.platform == 'win32'
|
||||
else 'surrogateescape')
|
||||
DEFAULT_CONFIG = {
|
||||
DEFAULT_CORE_CONFIG = {
|
||||
'install_signal_handlers': 1,
|
||||
'use_environment': 1,
|
||||
'use_hash_seed': 0,
|
||||
|
@ -338,7 +340,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
return out.split()
|
||||
|
||||
def check_config(self, testname, expected):
|
||||
expected = dict(self.DEFAULT_CONFIG, **expected)
|
||||
expected = dict(self.DEFAULT_CORE_CONFIG, **expected)
|
||||
|
||||
env = dict(os.environ)
|
||||
for key in list(env):
|
||||
|
@ -367,11 +369,39 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
|
|||
out, err = self.run_embedded_interpreter(testname, env=env)
|
||||
# Ignore err
|
||||
|
||||
config = {}
|
||||
core_config = {}
|
||||
main_config = {}
|
||||
for line in out.splitlines():
|
||||
key, value = line.split(' = ', 1)
|
||||
config[key] = value
|
||||
self.assertEqual(config, expected)
|
||||
match = self.CORE_CONFIG_REGEX.match(line)
|
||||
if match is not None:
|
||||
key = match.group(1)
|
||||
value = match.group(2)
|
||||
core_config[key] = value
|
||||
else:
|
||||
match = self.MAIN_CONFIG_REGEX.match(line)
|
||||
if match is None:
|
||||
raise ValueError(f"failed to parse line {line!r}")
|
||||
key = match.group(1)
|
||||
value = match.group(2)
|
||||
main_config[key] = value
|
||||
self.assertEqual(core_config, expected)
|
||||
|
||||
pycache_prefix = core_config['pycache_prefix']
|
||||
if pycache_prefix != NULL_STR:
|
||||
pycache_prefix = repr(pycache_prefix)
|
||||
else:
|
||||
pycache_prefix = "NULL"
|
||||
expected_main = {
|
||||
'install_signal_handlers': core_config['install_signal_handlers'],
|
||||
'argv': '[]',
|
||||
'prefix': repr(sys.prefix),
|
||||
'base_prefix': repr(sys.base_prefix),
|
||||
'base_exec_prefix': repr(sys.base_exec_prefix),
|
||||
'warnoptions': '[]',
|
||||
'xoptions': '{}',
|
||||
'pycache_prefix': pycache_prefix,
|
||||
}
|
||||
self.assertEqual(main_config, expected_main)
|
||||
|
||||
def test_init_default_config(self):
|
||||
self.check_config("init_default_config", {})
|
||||
|
|
|
@ -4702,6 +4702,15 @@ get_coreconfig(PyObject *self, PyObject *Py_UNUSED(args))
|
|||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
get_mainconfig(PyObject *self, PyObject *Py_UNUSED(args))
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_Get();
|
||||
const _PyMainInterpreterConfig *config = &interp->config;
|
||||
return _PyMainInterpreterConfig_AsDict(config);
|
||||
}
|
||||
|
||||
|
||||
#ifdef Py_REF_DEBUG
|
||||
static PyObject *
|
||||
negative_refcount(PyObject *self, PyObject *Py_UNUSED(args))
|
||||
|
@ -4948,6 +4957,7 @@ static PyMethodDef TestMethods[] = {
|
|||
{"EncodeLocaleEx", encode_locale_ex, METH_VARARGS},
|
||||
{"DecodeLocaleEx", decode_locale_ex, METH_VARARGS},
|
||||
{"get_coreconfig", get_coreconfig, METH_NOARGS},
|
||||
{"get_mainconfig", get_mainconfig, METH_NOARGS},
|
||||
#ifdef Py_REF_DEBUG
|
||||
{"negative_refcount", negative_refcount, METH_NOARGS},
|
||||
#endif
|
||||
|
|
|
@ -1441,31 +1441,88 @@ _PyMainInterpreterConfig_Copy(_PyMainInterpreterConfig *config,
|
|||
{
|
||||
_PyMainInterpreterConfig_Clear(config);
|
||||
|
||||
#define COPY_ATTR(ATTR) \
|
||||
#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
|
||||
#define COPY_OBJ_ATTR(OBJ_ATTR) \
|
||||
do { \
|
||||
if (config2->ATTR != NULL) { \
|
||||
config->ATTR = config_copy_attr(config2->ATTR); \
|
||||
if (config->ATTR == NULL) { \
|
||||
if (config2->OBJ_ATTR != NULL) { \
|
||||
config->OBJ_ATTR = config_copy_attr(config2->OBJ_ATTR); \
|
||||
if (config->OBJ_ATTR == NULL) { \
|
||||
return -1; \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
COPY_ATTR(argv);
|
||||
COPY_ATTR(executable);
|
||||
COPY_ATTR(prefix);
|
||||
COPY_ATTR(base_prefix);
|
||||
COPY_ATTR(exec_prefix);
|
||||
COPY_ATTR(base_exec_prefix);
|
||||
COPY_ATTR(warnoptions);
|
||||
COPY_ATTR(xoptions);
|
||||
COPY_ATTR(module_search_path);
|
||||
COPY_ATTR(pycache_prefix);
|
||||
COPY_ATTR(install_signal_handlers);
|
||||
COPY_OBJ_ATTR(argv);
|
||||
COPY_OBJ_ATTR(executable);
|
||||
COPY_OBJ_ATTR(prefix);
|
||||
COPY_OBJ_ATTR(base_prefix);
|
||||
COPY_OBJ_ATTR(exec_prefix);
|
||||
COPY_OBJ_ATTR(base_exec_prefix);
|
||||
COPY_OBJ_ATTR(warnoptions);
|
||||
COPY_OBJ_ATTR(xoptions);
|
||||
COPY_OBJ_ATTR(module_search_path);
|
||||
COPY_OBJ_ATTR(pycache_prefix);
|
||||
#undef COPY_ATTR
|
||||
#undef COPY_OBJ_ATTR
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PyObject*
|
||||
_PyMainInterpreterConfig_AsDict(const _PyMainInterpreterConfig *config)
|
||||
{
|
||||
PyObject *dict, *obj;
|
||||
int res;
|
||||
|
||||
dict = PyDict_New();
|
||||
if (dict == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#define SET_ITEM(KEY, ATTR) \
|
||||
do { \
|
||||
obj = config->ATTR; \
|
||||
if (obj == NULL) { \
|
||||
obj = Py_None; \
|
||||
} \
|
||||
res = PyDict_SetItemString(dict, (KEY), obj); \
|
||||
if (res < 0) { \
|
||||
goto fail; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
obj = PyLong_FromLong(config->install_signal_handlers);
|
||||
if (obj == NULL) {
|
||||
goto fail;
|
||||
}
|
||||
res = PyDict_SetItemString(dict, "install_signal_handlers", obj);
|
||||
Py_DECREF(obj);
|
||||
if (res < 0) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
SET_ITEM("argv", argv);
|
||||
SET_ITEM("executable", executable);
|
||||
SET_ITEM("prefix", prefix);
|
||||
SET_ITEM("base_prefix", base_prefix);
|
||||
SET_ITEM("exec_prefix", exec_prefix);
|
||||
SET_ITEM("base_exec_prefix", base_exec_prefix);
|
||||
SET_ITEM("warnoptions", warnoptions);
|
||||
SET_ITEM("xoptions", xoptions);
|
||||
SET_ITEM("module_search_path", module_search_path);
|
||||
SET_ITEM("pycache_prefix", pycache_prefix);
|
||||
|
||||
return dict;
|
||||
|
||||
fail:
|
||||
Py_DECREF(dict);
|
||||
return NULL;
|
||||
|
||||
#undef SET_ITEM
|
||||
}
|
||||
|
||||
|
||||
_PyInitError
|
||||
_PyMainInterpreterConfig_Read(_PyMainInterpreterConfig *main_config,
|
||||
const _PyCoreConfig *config)
|
||||
|
|
|
@ -293,7 +293,7 @@ static int test_initialize_pymain(void)
|
|||
|
||||
|
||||
static void
|
||||
dump_config(void)
|
||||
dump_core_config(void)
|
||||
{
|
||||
#define ASSERT_EQUAL(a, b) \
|
||||
if ((a) != (b)) { \
|
||||
|
@ -309,37 +309,37 @@ dump_config(void)
|
|||
PyInterpreterState *interp = _PyInterpreterState_Get();
|
||||
_PyCoreConfig *config = &interp->core_config;
|
||||
|
||||
printf("install_signal_handlers = %i\n", config->install_signal_handlers);
|
||||
printf("core_config[install_signal_handlers] = %i\n", config->install_signal_handlers);
|
||||
|
||||
printf("use_environment = %i\n", config->use_environment);
|
||||
printf("core_config[use_environment] = %i\n", config->use_environment);
|
||||
ASSERT_EQUAL(config->use_environment, !Py_IgnoreEnvironmentFlag);
|
||||
|
||||
printf("use_hash_seed = %i\n", config->use_hash_seed);
|
||||
printf("hash_seed = %lu\n", config->hash_seed);
|
||||
printf("core_config[use_hash_seed] = %i\n", config->use_hash_seed);
|
||||
printf("core_config[hash_seed] = %lu\n", config->hash_seed);
|
||||
|
||||
printf("allocator = %s\n", config->allocator);
|
||||
printf("core_config[allocator] = %s\n", config->allocator);
|
||||
|
||||
printf("dev_mode = %i\n", config->dev_mode);
|
||||
printf("faulthandler = %i\n", config->faulthandler);
|
||||
printf("tracemalloc = %i\n", config->tracemalloc);
|
||||
printf("import_time = %i\n", config->import_time);
|
||||
printf("show_ref_count = %i\n", config->show_ref_count);
|
||||
printf("show_alloc_count = %i\n", config->show_alloc_count);
|
||||
printf("dump_refs = %i\n", config->dump_refs);
|
||||
printf("malloc_stats = %i\n", config->malloc_stats);
|
||||
printf("core_config[dev_mode] = %i\n", config->dev_mode);
|
||||
printf("core_config[faulthandler] = %i\n", config->faulthandler);
|
||||
printf("core_config[tracemalloc] = %i\n", config->tracemalloc);
|
||||
printf("core_config[import_time] = %i\n", config->import_time);
|
||||
printf("core_config[show_ref_count] = %i\n", config->show_ref_count);
|
||||
printf("core_config[show_alloc_count] = %i\n", config->show_alloc_count);
|
||||
printf("core_config[dump_refs] = %i\n", config->dump_refs);
|
||||
printf("core_config[malloc_stats] = %i\n", config->malloc_stats);
|
||||
|
||||
printf("filesystem_encoding = %s\n", config->filesystem_encoding);
|
||||
printf("filesystem_errors = %s\n", config->filesystem_errors);
|
||||
printf("coerce_c_locale = %i\n", config->coerce_c_locale);
|
||||
printf("coerce_c_locale_warn = %i\n", config->coerce_c_locale_warn);
|
||||
printf("utf8_mode = %i\n", config->utf8_mode);
|
||||
printf("core_config[filesystem_encoding] = %s\n", config->filesystem_encoding);
|
||||
printf("core_config[filesystem_errors] = %s\n", config->filesystem_errors);
|
||||
printf("core_config[coerce_c_locale] = %i\n", config->coerce_c_locale);
|
||||
printf("core_config[coerce_c_locale_warn] = %i\n", config->coerce_c_locale_warn);
|
||||
printf("core_config[utf8_mode] = %i\n", config->utf8_mode);
|
||||
|
||||
printf("pycache_prefix = %ls\n", config->pycache_prefix);
|
||||
printf("program_name = %ls\n", config->program_name);
|
||||
printf("core_config[pycache_prefix] = %ls\n", config->pycache_prefix);
|
||||
printf("core_config[program_name] = %ls\n", config->program_name);
|
||||
ASSERT_STR_EQUAL(config->program_name, Py_GetProgramName());
|
||||
|
||||
printf("argc = %i\n", config->argc);
|
||||
printf("argv = [");
|
||||
printf("core_config[argc] = %i\n", config->argc);
|
||||
printf("core_config[argv] = [");
|
||||
for (int i=0; i < config->argc; i++) {
|
||||
if (i) {
|
||||
printf(", ");
|
||||
|
@ -348,7 +348,7 @@ dump_config(void)
|
|||
}
|
||||
printf("]\n");
|
||||
|
||||
printf("program = %ls\n", config->program);
|
||||
printf("core_config[program] = %ls\n", config->program);
|
||||
/* FIXME: test xoptions */
|
||||
/* FIXME: test warnoptions */
|
||||
/* FIXME: test module_search_path_env */
|
||||
|
@ -361,36 +361,75 @@ dump_config(void)
|
|||
/* FIXME: test base_exec_prefix */
|
||||
/* FIXME: test dll_path */
|
||||
|
||||
printf("isolated = %i\n", config->isolated);
|
||||
printf("core_config[isolated] = %i\n", config->isolated);
|
||||
ASSERT_EQUAL(config->isolated, Py_IsolatedFlag);
|
||||
printf("site_import = %i\n", config->site_import);
|
||||
printf("bytes_warning = %i\n", config->bytes_warning);
|
||||
printf("inspect = %i\n", config->inspect);
|
||||
printf("interactive = %i\n", config->interactive);
|
||||
printf("optimization_level = %i\n", config->optimization_level);
|
||||
printf("parser_debug = %i\n", config->parser_debug);
|
||||
printf("write_bytecode = %i\n", config->write_bytecode);
|
||||
printf("verbose = %i\n", config->verbose);
|
||||
printf("core_config[site_import] = %i\n", config->site_import);
|
||||
printf("core_config[bytes_warning] = %i\n", config->bytes_warning);
|
||||
printf("core_config[inspect] = %i\n", config->inspect);
|
||||
printf("core_config[interactive] = %i\n", config->interactive);
|
||||
printf("core_config[optimization_level] = %i\n", config->optimization_level);
|
||||
printf("core_config[parser_debug] = %i\n", config->parser_debug);
|
||||
printf("core_config[write_bytecode] = %i\n", config->write_bytecode);
|
||||
printf("core_config[verbose] = %i\n", config->verbose);
|
||||
ASSERT_EQUAL(config->verbose, Py_VerboseFlag);
|
||||
printf("quiet = %i\n", config->quiet);
|
||||
printf("user_site_directory = %i\n", config->user_site_directory);
|
||||
printf("buffered_stdio = %i\n", config->buffered_stdio);
|
||||
printf("core_config[quiet] = %i\n", config->quiet);
|
||||
printf("core_config[user_site_directory] = %i\n", config->user_site_directory);
|
||||
printf("core_config[buffered_stdio] = %i\n", config->buffered_stdio);
|
||||
ASSERT_EQUAL(config->buffered_stdio, !Py_UnbufferedStdioFlag);
|
||||
printf("stdio_encoding = %s\n", config->stdio_encoding);
|
||||
printf("stdio_errors = %s\n", config->stdio_errors);
|
||||
printf("core_config[stdio_encoding] = %s\n", config->stdio_encoding);
|
||||
printf("core_config[stdio_errors] = %s\n", config->stdio_errors);
|
||||
|
||||
/* FIXME: test legacy_windows_fs_encoding */
|
||||
/* FIXME: test legacy_windows_stdio */
|
||||
|
||||
printf("_install_importlib = %i\n", config->_install_importlib);
|
||||
printf("_check_hash_pycs_mode = %s\n", config->_check_hash_pycs_mode);
|
||||
printf("_frozen = %i\n", config->_frozen);
|
||||
printf("core_config[_install_importlib] = %i\n", config->_install_importlib);
|
||||
printf("core_config[_check_hash_pycs_mode] = %s\n", config->_check_hash_pycs_mode);
|
||||
printf("core_config[_frozen] = %i\n", config->_frozen);
|
||||
|
||||
#undef ASSERT_EQUAL
|
||||
#undef ASSERT_STR_EQUAL
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_main_config(void)
|
||||
{
|
||||
PyInterpreterState *interp = _PyInterpreterState_Get();
|
||||
_PyMainInterpreterConfig *config = &interp->config;
|
||||
|
||||
printf("main_config[install_signal_handlers] = %i\n", config->install_signal_handlers);
|
||||
#define DUMP_ATTR(ATTR) \
|
||||
do { \
|
||||
if (config->ATTR != NULL) { \
|
||||
PySys_FormatStdout("main_config[" #ATTR "] = %R\n", config->ATTR); \
|
||||
} \
|
||||
else { \
|
||||
PySys_FormatStdout("main_config[" #ATTR "] = NULL\n"); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
DUMP_ATTR(argv);
|
||||
/* FIXME: DUMP_ATTR(executable); */
|
||||
DUMP_ATTR(prefix);
|
||||
DUMP_ATTR(base_prefix);
|
||||
DUMP_ATTR(base_exec_prefix);
|
||||
DUMP_ATTR(warnoptions);
|
||||
DUMP_ATTR(xoptions);
|
||||
/* FIXME: DUMP_ATTR(module_search_path); */
|
||||
DUMP_ATTR(pycache_prefix);
|
||||
|
||||
#undef DUMP_ATTR
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dump_config(void)
|
||||
{
|
||||
dump_core_config();
|
||||
dump_main_config();
|
||||
}
|
||||
|
||||
|
||||
static int test_init_default_config(void)
|
||||
{
|
||||
_testembed_Py_Initialize();
|
||||
|
|
Loading…
Reference in New Issue