From f3cb81431574453aac3b6dcadb3120331e6a8f1c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 5 Nov 2020 18:12:33 +0100 Subject: [PATCH] bpo-42260: Add _PyConfig_FromDict() (GH-23167) * Rename config_as_dict() to _PyConfig_AsDict(). * Add 'module_search_paths_set' to _PyConfig_AsDict(). * Add _PyConfig_FromDict(). * Add get_config() and set_config() to _testinternalcapi. * Add config_check_consistency(). --- Include/internal/pycore_initconfig.h | 3 + Lib/test/_test_embed_set_config.py | 243 ++++++++++++++++ Lib/test/test_embed.py | 14 + Modules/_testinternalcapi.c | 39 ++- Python/initconfig.c | 410 +++++++++++++++++++++++---- Python/sysmodule.c | 4 +- 6 files changed, 653 insertions(+), 60 deletions(-) create mode 100644 Lib/test/_test_embed_set_config.py diff --git a/Include/internal/pycore_initconfig.h b/Include/internal/pycore_initconfig.h index df7ad779f47..325be5494d4 100644 --- a/Include/internal/pycore_initconfig.h +++ b/Include/internal/pycore_initconfig.h @@ -158,6 +158,9 @@ extern PyStatus _PyConfig_SetPyArgv( PyConfig *config, const _PyArgv *args); +PyAPI_FUNC(PyObject*) _PyConfig_AsDict(const PyConfig *config); +PyAPI_FUNC(int) _PyConfig_FromDict(PyConfig *config, PyObject *dict); + /* --- Function used for testing ---------------------------------- */ diff --git a/Lib/test/_test_embed_set_config.py b/Lib/test/_test_embed_set_config.py new file mode 100644 index 00000000000..7c913811ded --- /dev/null +++ b/Lib/test/_test_embed_set_config.py @@ -0,0 +1,243 @@ +# bpo-42260: Test _PyInterpreterState_GetConfigCopy() +# and _PyInterpreterState_SetConfig(). +# +# Test run in a subinterpreter since set_config(get_config()) +# does reset sys attributes to their state of the Python startup +# (before the site module is run). + +import _testinternalcapi +import os +import sys +import unittest + + +MS_WINDOWS = (os.name == 'nt') +MAX_HASH_SEED = 4294967295 + +class SetConfigTests(unittest.TestCase): + def setUp(self): + self.old_config = _testinternalcapi.get_config() + self.sys_copy = dict(sys.__dict__) + + def tearDown(self): + self.set_config(parse_argv=0) + sys.__dict__.clear() + sys.__dict__.update(self.sys_copy) + + def set_config(self, **kwargs): + _testinternalcapi.set_config(self.old_config | kwargs) + + def check(self, **kwargs): + self.set_config(**kwargs) + for key, value in kwargs.items(): + self.assertEqual(getattr(sys, key), value, + (key, value)) + + def test_set_invalid(self): + invalid_uint = -1 + NULL = None + invalid_wstr = NULL + # PyWideStringList strings must be non-NULL + invalid_wstrlist = ["abc", NULL, "def"] + + type_tests = [] + value_tests = [ + # enum + ('_config_init', 0), + ('_config_init', 4), + # unsigned long + ("hash_seed", -1), + ("hash_seed", MAX_HASH_SEED + 1), + ] + + # int (unsigned) + options = [ + '_config_init', + 'isolated', + 'use_environment', + 'dev_mode', + 'install_signal_handlers', + 'use_hash_seed', + 'faulthandler', + 'tracemalloc', + 'import_time', + 'show_ref_count', + 'dump_refs', + 'malloc_stats', + 'parse_argv', + 'site_import', + 'bytes_warning', + 'inspect', + 'interactive', + 'optimization_level', + 'parser_debug', + 'write_bytecode', + 'verbose', + 'quiet', + 'user_site_directory', + 'configure_c_stdio', + 'buffered_stdio', + 'pathconfig_warnings', + 'module_search_paths_set', + 'skip_source_first_line', + '_install_importlib', + '_init_main', + '_isolated_interpreter', + ] + if MS_WINDOWS: + options.append('legacy_windows_stdio') + for key in options: + value_tests.append((key, invalid_uint)) + type_tests.append((key, "abc")) + type_tests.append((key, 2.0)) + + # wchar_t* + for key in ( + 'filesystem_encoding', + 'filesystem_errors', + 'stdio_encoding', + 'stdio_errors', + 'check_hash_pycs_mode', + 'program_name', + 'platlibdir', + 'executable', + 'base_executable', + 'prefix', + 'base_prefix', + 'exec_prefix', + 'base_exec_prefix', + # optional wstr: + # 'pythonpath_env' + # 'home', + # 'pycache_prefix' + # 'run_command' + # 'run_module' + # 'run_filename' + ): + value_tests.append((key, invalid_wstr)) + type_tests.append((key, b'bytes')) + type_tests.append((key, 123)) + + # PyWideStringList + for key in ( + 'orig_argv', + 'argv', + 'xoptions', + 'warnoptions', + 'module_search_paths', + ): + value_tests.append((key, invalid_wstrlist)) + type_tests.append((key, 123)) + type_tests.append((key, "abc")) + type_tests.append((key, [123])) + type_tests.append((key, [b"bytes"])) + + + if MS_WINDOWS: + value_tests.append(('legacy_windows_stdio', invalid_uint)) + + for exc_type, tests in ( + (ValueError, value_tests), + (TypeError, type_tests), + ): + for key, value in tests: + config = self.old_config | {key: value} + with self.subTest(key=key, value=value, exc_type=exc_type): + with self.assertRaises(exc_type): + _testinternalcapi.set_config(config) + + def test_flags(self): + for sys_attr, key, value in ( + ("debug", "parser_debug", 1), + ("inspect", "inspect", 2), + ("interactive", "interactive", 3), + ("optimize", "optimization_level", 4), + ("verbose", "verbose", 1), + ("bytes_warning", "bytes_warning", 10), + ("quiet", "quiet", 11), + ("isolated", "isolated", 12), + ): + with self.subTest(sys=sys_attr, key=key, value=value): + self.set_config(**{key: value, 'parse_argv': 0}) + self.assertEqual(getattr(sys.flags, sys_attr), value) + + self.set_config(write_bytecode=0) + self.assertEqual(sys.flags.dont_write_bytecode, True) + self.assertEqual(sys.dont_write_bytecode, True) + + self.set_config(write_bytecode=1) + self.assertEqual(sys.flags.dont_write_bytecode, False) + self.assertEqual(sys.dont_write_bytecode, False) + + self.set_config(user_site_directory=0, isolated=0) + self.assertEqual(sys.flags.no_user_site, 1) + self.set_config(user_site_directory=1, isolated=0) + self.assertEqual(sys.flags.no_user_site, 0) + + self.set_config(site_import=0) + self.assertEqual(sys.flags.no_site, 1) + self.set_config(site_import=1) + self.assertEqual(sys.flags.no_site, 0) + + self.set_config(dev_mode=0) + self.assertEqual(sys.flags.dev_mode, False) + self.set_config(dev_mode=1) + self.assertEqual(sys.flags.dev_mode, True) + + self.set_config(use_environment=0, isolated=0) + self.assertEqual(sys.flags.ignore_environment, 1) + self.set_config(use_environment=1, isolated=0) + self.assertEqual(sys.flags.ignore_environment, 0) + + self.set_config(use_hash_seed=1, hash_seed=0) + self.assertEqual(sys.flags.hash_randomization, 0) + self.set_config(use_hash_seed=0, hash_seed=0) + self.assertEqual(sys.flags.hash_randomization, 1) + self.set_config(use_hash_seed=1, hash_seed=123) + self.assertEqual(sys.flags.hash_randomization, 1) + + def test_options(self): + self.check(warnoptions=[]) + self.check(warnoptions=["default", "ignore"]) + + self.set_config(xoptions=[]) + self.assertEqual(sys._xoptions, {}) + self.set_config(xoptions=["dev", "tracemalloc=5"]) + self.assertEqual(sys._xoptions, {"dev": True, "tracemalloc": "5"}) + + def test_pathconfig(self): + self.check( + executable='executable', + prefix="prefix", + base_prefix="base_prefix", + exec_prefix="exec_prefix", + base_exec_prefix="base_exec_prefix", + platlibdir="platlibdir") + + self.set_config(base_executable="base_executable") + self.assertEqual(sys._base_executable, "base_executable") + + def test_path(self): + self.set_config(module_search_paths_set=1, + module_search_paths=['a', 'b', 'c']) + self.assertEqual(sys.path, ['a', 'b', 'c']) + + # Leave sys.path unchanged if module_search_paths_set=0 + self.set_config(module_search_paths_set=0, + module_search_paths=['new_path']) + self.assertEqual(sys.path, ['a', 'b', 'c']) + + def test_argv(self): + self.set_config(parse_argv=0, + argv=['python_program', 'args'], + orig_argv=['orig', 'orig_args']) + self.assertEqual(sys.argv, ['python_program', 'args']) + self.assertEqual(sys.orig_argv, ['orig', 'orig_args']) + + def test_pycache_prefix(self): + self.check(pycache_prefix=None) + self.check(pycache_prefix="pycache_prefix") + + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py index 36a0e77e14c..91820615193 100644 --- a/Lib/test/test_embed.py +++ b/Lib/test/test_embed.py @@ -30,6 +30,8 @@ API_PYTHON = 2 # _PyCoreConfig_InitIsolatedConfig() API_ISOLATED = 3 +MAX_HASH_SEED = 4294967295 + def debug_build(program): program = os.path.basename(program) @@ -382,6 +384,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): 'exec_prefix': GET_DEFAULT_CONFIG, 'base_exec_prefix': GET_DEFAULT_CONFIG, 'module_search_paths': GET_DEFAULT_CONFIG, + 'module_search_paths_set': 1, 'platlibdir': sys.platlibdir, 'site_import': 1, @@ -1408,6 +1411,17 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase): # ignore output +class SetConfigTests(unittest.TestCase): + def test_set_config(self): + # bpo-42260: Test _PyInterpreterState_SetConfig() + cmd = [sys.executable, '-I', '-m', 'test._test_embed_set_config'] + proc = subprocess.run(cmd, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + self.assertEqual(proc.returncode, 0, + (proc.returncode, proc.stdout, proc.stderr)) + + class AuditingTests(EmbeddingTestsMixin, unittest.TestCase): def test_open_code_hook(self): self.run_embedded_interpreter("test_open_code_hook") diff --git a/Modules/_testinternalcapi.c b/Modules/_testinternalcapi.c index ad74af8363e..be144bfba02 100644 --- a/Modules/_testinternalcapi.c +++ b/Modules/_testinternalcapi.c @@ -13,9 +13,10 @@ #include "Python.h" #include "pycore_bitutils.h" // _Py_bswap32() -#include "pycore_initconfig.h" // _Py_GetConfigsAsDict() -#include "pycore_hashtable.h" // _Py_hashtable_new() #include "pycore_gc.h" // PyGC_Head +#include "pycore_hashtable.h" // _Py_hashtable_new() +#include "pycore_initconfig.h" // _Py_GetConfigsAsDict() +#include "pycore_interp.h" // _PyInterpreterState_GetConfigCopy() static PyObject * @@ -231,6 +232,38 @@ test_hashtable(PyObject *self, PyObject *Py_UNUSED(args)) } +static PyObject * +test_get_config(PyObject *Py_UNUSED(self), PyObject *Py_UNUSED(args)) +{ + PyConfig config; + PyConfig_InitIsolatedConfig(&config); + if (_PyInterpreterState_GetConfigCopy(&config) < 0) { + PyConfig_Clear(&config); + return NULL; + } + PyObject *dict = _PyConfig_AsDict(&config); + PyConfig_Clear(&config); + return dict; +} + + +static PyObject * +test_set_config(PyObject *Py_UNUSED(self), PyObject *dict) +{ + PyConfig config; + PyConfig_InitIsolatedConfig(&config); + if (_PyConfig_FromDict(&config, dict) < 0) { + PyConfig_Clear(&config); + return NULL; + } + if (_PyInterpreterState_SetConfig(&config) < 0) { + return NULL; + } + PyConfig_Clear(&config); + Py_RETURN_NONE; +} + + static PyMethodDef TestMethods[] = { {"get_configs", get_configs, METH_NOARGS}, {"get_recursion_depth", get_recursion_depth, METH_NOARGS}, @@ -238,6 +271,8 @@ static PyMethodDef TestMethods[] = { {"test_popcount", test_popcount, METH_NOARGS}, {"test_bit_length", test_bit_length, METH_NOARGS}, {"test_hashtable", test_hashtable, METH_NOARGS}, + {"get_config", test_get_config, METH_NOARGS}, + {"set_config", test_set_config, METH_O}, {NULL, NULL} /* sentinel */ }; diff --git a/Python/initconfig.c b/Python/initconfig.c index de496ac7b52..d54d5b7a999 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -577,6 +577,74 @@ Py_GetArgcArgv(int *argc, wchar_t ***argv) ? _PyStatus_ERR("cannot decode " NAME) \ : _PyStatus_NO_MEMORY()) +#define MAX_HASH_SEED 4294967295UL + + +#ifndef NDEBUG +static int +config_check_consistency(const PyConfig *config) +{ + /* Check config consistency */ + assert(config->isolated >= 0); + assert(config->use_environment >= 0); + assert(config->dev_mode >= 0); + assert(config->install_signal_handlers >= 0); + assert(config->use_hash_seed >= 0); + assert(config->hash_seed <= MAX_HASH_SEED); + assert(config->faulthandler >= 0); + assert(config->tracemalloc >= 0); + assert(config->import_time >= 0); + assert(config->show_ref_count >= 0); + assert(config->dump_refs >= 0); + assert(config->malloc_stats >= 0); + assert(config->site_import >= 0); + assert(config->bytes_warning >= 0); + assert(config->inspect >= 0); + assert(config->interactive >= 0); + assert(config->optimization_level >= 0); + assert(config->parser_debug >= 0); + assert(config->write_bytecode >= 0); + assert(config->verbose >= 0); + assert(config->quiet >= 0); + assert(config->user_site_directory >= 0); + assert(config->parse_argv >= 0); + assert(config->configure_c_stdio >= 0); + assert(config->buffered_stdio >= 0); + assert(config->program_name != NULL); + assert(_PyWideStringList_CheckConsistency(&config->orig_argv)); + assert(_PyWideStringList_CheckConsistency(&config->argv)); + /* sys.argv must be non-empty: empty argv is replaced with [''] */ + assert(config->argv.length >= 1); + assert(_PyWideStringList_CheckConsistency(&config->xoptions)); + assert(_PyWideStringList_CheckConsistency(&config->warnoptions)); + assert(_PyWideStringList_CheckConsistency(&config->module_search_paths)); + assert(config->module_search_paths_set >= 0); + if (config->_install_importlib) { + /* don't check config->module_search_paths */ + assert(config->executable != NULL); + assert(config->base_executable != NULL); + assert(config->prefix != NULL); + assert(config->base_prefix != NULL); + assert(config->exec_prefix != NULL); + assert(config->base_exec_prefix != NULL); + } + assert(config->platlibdir != NULL); + assert(config->filesystem_encoding != NULL); + assert(config->filesystem_errors != NULL); + assert(config->stdio_encoding != NULL); + assert(config->stdio_errors != NULL); +#ifdef MS_WINDOWS + assert(config->legacy_windows_stdio >= 0); +#endif + /* -c and -m options are exclusive */ + assert(!(config->run_command != NULL && config->run_module != NULL)); + assert(config->check_hash_pycs_mode != NULL); + assert(config->_install_importlib >= 0); + assert(config->pathconfig_warnings >= 0); + return 1; +} +#endif + /* Free memory allocated in config, but don't clear all attributes */ void @@ -880,8 +948,8 @@ _PyConfig_Copy(PyConfig *config, const PyConfig *config2) } -static PyObject * -config_as_dict(const PyConfig *config) +PyObject * +_PyConfig_AsDict(const PyConfig *config) { PyObject *dict = PyDict_New(); if (dict == NULL) { @@ -936,6 +1004,7 @@ config_as_dict(const PyConfig *config) SET_ITEM_WSTRLIST(warnoptions); SET_ITEM_WSTR(pythonpath_env); SET_ITEM_WSTR(home); + SET_ITEM_INT(module_search_paths_set); SET_ITEM_WSTRLIST(module_search_paths); SET_ITEM_WSTR(executable); SET_ITEM_WSTR(base_executable); @@ -987,6 +1056,285 @@ fail: } +static PyObject* +config_dict_get(PyObject *dict, const char *name) +{ + PyObject *item = PyDict_GetItemString(dict, name); + if (item == NULL) { + PyErr_Format(PyExc_ValueError, "missing config key: %s", name); + return NULL; + } + return item; +} + + +static void +config_dict_invalid_value(const char *name) +{ + PyErr_Format(PyExc_ValueError, "invalid config value: %s", name); +} + + +static void +config_dict_invalid_type(const char *name) +{ + PyErr_Format(PyExc_TypeError, "invalid config type: %s", name); +} + + +static int +config_dict_get_int(PyObject *dict, const char *name, int *result) +{ + PyObject *item = config_dict_get(dict, name); + if (item == NULL) { + return -1; + } + int value = _PyLong_AsInt(item); + if (value == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_TypeError)) { + config_dict_invalid_type(name); + } + else { + config_dict_invalid_value(name); + } + return -1; + } + *result = value; + return 0; +} + + +static int +config_dict_get_ulong(PyObject *dict, const char *name, unsigned long *result) +{ + PyObject *item = config_dict_get(dict, name); + if (item == NULL) { + return -1; + } + unsigned long value = PyLong_AsUnsignedLong(item); + if (value == (unsigned long)-1 && PyErr_Occurred()) { + config_dict_invalid_value(name); + return -1; + } + *result = value; + return 0; +} + + +static int +config_dict_get_wstr(PyObject *dict, const char *name, PyConfig *config, + wchar_t **result) +{ + PyObject *item = config_dict_get(dict, name); + if (item == NULL) { + return -1; + } + PyStatus status; + if (item == Py_None) { + status = PyConfig_SetString(config, result, NULL); + } + else if (!PyUnicode_Check(item)) { + config_dict_invalid_type(name); + return -1; + } + else { + wchar_t *wstr = PyUnicode_AsWideCharString(item, NULL); + if (wstr == NULL) { + return -1; + } + status = PyConfig_SetString(config, result, wstr); + PyMem_Free(wstr); + } + if (_PyStatus_EXCEPTION(status)) { + PyErr_NoMemory(); + return -1; + } + return 0; +} + + +static int +config_dict_get_wstrlist(PyObject *dict, const char *name, PyConfig *config, + PyWideStringList *result) +{ + PyObject *list = config_dict_get(dict, name); + if (list == NULL) { + return -1; + } + + if (!PyList_CheckExact(list)) { + config_dict_invalid_type(name); + return -1; + } + + PyWideStringList wstrlist = _PyWideStringList_INIT; + for (Py_ssize_t i=0; i < PyList_GET_SIZE(list); i++) { + PyObject *item = PyList_GET_ITEM(list, i); + + if (item == Py_None) { + config_dict_invalid_value(name); + goto error; + } + else if (!PyUnicode_Check(item)) { + config_dict_invalid_type(name); + goto error; + } + wchar_t *wstr = PyUnicode_AsWideCharString(item, NULL); + if (wstr == NULL) { + goto error; + } + PyStatus status = PyWideStringList_Append(&wstrlist, wstr); + PyMem_Free(wstr); + if (_PyStatus_EXCEPTION(status)) { + PyErr_NoMemory(); + goto error; + } + } + + if (_PyWideStringList_Copy(result, &wstrlist) < 0) { + PyErr_NoMemory(); + goto error; + } + _PyWideStringList_Clear(&wstrlist); + return 0; + +error: + _PyWideStringList_Clear(&wstrlist); + return -1; +} + + +int +_PyConfig_FromDict(PyConfig *config, PyObject *dict) +{ + if (!PyDict_Check(dict)) { + PyErr_SetString(PyExc_TypeError, "dict expected"); + return -1; + } + +#define CHECK_VALUE(NAME, TEST) \ + if (!(TEST)) { \ + config_dict_invalid_value(NAME); \ + return -1; \ + } +#define GET_UINT(KEY) \ + do { \ + if (config_dict_get_int(dict, #KEY, &config->KEY) < 0) { \ + return -1; \ + } \ + CHECK_VALUE(#KEY, config->KEY >= 0); \ + } while (0) +#define GET_WSTR(KEY) \ + do { \ + if (config_dict_get_wstr(dict, #KEY, config, &config->KEY) < 0) { \ + return -1; \ + } \ + CHECK_VALUE(#KEY, config->KEY != NULL); \ + } while (0) +#define GET_WSTR_OPT(KEY) \ + do { \ + if (config_dict_get_wstr(dict, #KEY, config, &config->KEY) < 0) { \ + return -1; \ + } \ + } while (0) +#define GET_WSTRLIST(KEY) \ + do { \ + if (config_dict_get_wstrlist(dict, #KEY, config, &config->KEY) < 0) { \ + return -1; \ + } \ + } while (0) + + GET_UINT(_config_init); + CHECK_VALUE("_config_init", + config->_config_init == _PyConfig_INIT_COMPAT + || config->_config_init == _PyConfig_INIT_PYTHON + || config->_config_init == _PyConfig_INIT_ISOLATED); + GET_UINT(isolated); + GET_UINT(use_environment); + GET_UINT(dev_mode); + GET_UINT(install_signal_handlers); + GET_UINT(use_hash_seed); + if (config_dict_get_ulong(dict, "hash_seed", &config->hash_seed) < 0) { + return -1; + } + CHECK_VALUE("hash_seed", config->hash_seed <= MAX_HASH_SEED); + GET_UINT(faulthandler); + GET_UINT(tracemalloc); + GET_UINT(import_time); + GET_UINT(show_ref_count); + GET_UINT(dump_refs); + GET_UINT(malloc_stats); + GET_WSTR(filesystem_encoding); + GET_WSTR(filesystem_errors); + GET_WSTR_OPT(pycache_prefix); + GET_UINT(parse_argv); + GET_WSTRLIST(orig_argv); + GET_WSTRLIST(argv); + GET_WSTRLIST(xoptions); + GET_WSTRLIST(warnoptions); + GET_UINT(site_import); + GET_UINT(bytes_warning); + GET_UINT(inspect); + GET_UINT(interactive); + GET_UINT(optimization_level); + GET_UINT(parser_debug); + GET_UINT(write_bytecode); + GET_UINT(verbose); + GET_UINT(quiet); + GET_UINT(user_site_directory); + GET_UINT(configure_c_stdio); + GET_UINT(buffered_stdio); + GET_WSTR(stdio_encoding); + GET_WSTR(stdio_errors); +#ifdef MS_WINDOWS + GET_UINT(legacy_windows_stdio); +#endif + GET_WSTR(check_hash_pycs_mode); + + GET_UINT(pathconfig_warnings); + GET_WSTR(program_name); + GET_WSTR_OPT(pythonpath_env); + GET_WSTR_OPT(home); + GET_WSTR(platlibdir); + + GET_UINT(module_search_paths_set); + GET_WSTRLIST(module_search_paths); + if (config->_install_importlib) { + GET_WSTR(executable); + GET_WSTR(base_executable); + GET_WSTR(prefix); + GET_WSTR(base_prefix); + GET_WSTR(exec_prefix); + GET_WSTR(base_exec_prefix); + } + else { + GET_WSTR_OPT(executable); + GET_WSTR_OPT(base_executable); + GET_WSTR_OPT(prefix); + GET_WSTR_OPT(base_prefix); + GET_WSTR_OPT(exec_prefix); + GET_WSTR_OPT(base_exec_prefix); + } + + GET_UINT(skip_source_first_line); + GET_WSTR_OPT(run_command); + GET_WSTR_OPT(run_module); + GET_WSTR_OPT(run_filename); + + GET_UINT(_install_importlib); + GET_UINT(_init_main); + GET_UINT(_isolated_interpreter); + + assert(config_check_consistency(config)); + +#undef CHECK_VALUE +#undef GET_UINT +#undef GET_WSTR +#undef GET_WSTR_OPT + return 0; +} + + static const char* config_get_env(const PyConfig *config, const char *name) { @@ -1254,7 +1602,6 @@ config_init_home(PyConfig *config) L"PYTHONHOME", "PYTHONHOME"); } - static PyStatus config_init_hash_seed(PyConfig *config) { @@ -1268,7 +1615,7 @@ config_init_hash_seed(PyConfig *config) errno = 0; seed = strtoul(seed_text, (char **)&endptr, 10); if (*endptr != '\0' - || seed > 4294967295UL + || seed > MAX_HASH_SEED || (errno == ERANGE && seed == ULONG_MAX)) { return _PyStatus_ERR("PYTHONHASHSEED must be \"random\" " @@ -2532,58 +2879,7 @@ PyConfig_Read(PyConfig *config) goto done; } - /* Check config consistency */ - assert(config->isolated >= 0); - assert(config->use_environment >= 0); - assert(config->dev_mode >= 0); - assert(config->install_signal_handlers >= 0); - assert(config->use_hash_seed >= 0); - assert(config->faulthandler >= 0); - assert(config->tracemalloc >= 0); - assert(config->site_import >= 0); - assert(config->bytes_warning >= 0); - assert(config->inspect >= 0); - assert(config->interactive >= 0); - assert(config->optimization_level >= 0); - assert(config->parser_debug >= 0); - assert(config->write_bytecode >= 0); - assert(config->verbose >= 0); - assert(config->quiet >= 0); - assert(config->user_site_directory >= 0); - assert(config->parse_argv >= 0); - assert(config->configure_c_stdio >= 0); - assert(config->buffered_stdio >= 0); - assert(config->program_name != NULL); - assert(_PyWideStringList_CheckConsistency(&config->argv)); - /* sys.argv must be non-empty: empty argv is replaced with [''] */ - assert(config->argv.length >= 1); - assert(_PyWideStringList_CheckConsistency(&config->xoptions)); - assert(_PyWideStringList_CheckConsistency(&config->warnoptions)); - assert(_PyWideStringList_CheckConsistency(&config->module_search_paths)); - if (config->_install_importlib) { - assert(config->module_search_paths_set != 0); - /* don't check config->module_search_paths */ - assert(config->executable != NULL); - assert(config->base_executable != NULL); - assert(config->prefix != NULL); - assert(config->base_prefix != NULL); - assert(config->exec_prefix != NULL); - assert(config->base_exec_prefix != NULL); - } - assert(config->platlibdir != NULL); - assert(config->filesystem_encoding != NULL); - assert(config->filesystem_errors != NULL); - assert(config->stdio_encoding != NULL); - assert(config->stdio_errors != NULL); -#ifdef MS_WINDOWS - assert(config->legacy_windows_stdio >= 0); -#endif - /* -c and -m options are exclusive */ - assert(!(config->run_command != NULL && config->run_module != NULL)); - assert(config->check_hash_pycs_mode != NULL); - assert(config->_install_importlib >= 0); - assert(config->pathconfig_warnings >= 0); - assert(_PyWideStringList_CheckConsistency(&config->orig_argv)); + assert(config_check_consistency(config)); status = _PyStatus_OK(); @@ -2628,7 +2924,7 @@ _Py_GetConfigsAsDict(void) /* core config */ const PyConfig *config = _PyInterpreterState_GetConfig(tstate->interp); - dict = config_as_dict(config); + dict = _PyConfig_AsDict(config); if (dict == NULL) { goto error; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 60b24946512..ae4f0eeb2ee 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2922,7 +2922,9 @@ _PySys_UpdateConfig(PyThreadState *tstate) #define SET_SYS_FROM_WSTR(KEY, VALUE) \ SET_SYS(KEY, PyUnicode_FromWideChar(VALUE, -1)); - COPY_LIST("path", config->module_search_paths); + if (config->module_search_paths_set) { + COPY_LIST("path", config->module_search_paths); + } SET_SYS_FROM_WSTR("executable", config->executable); SET_SYS_FROM_WSTR("_base_executable", config->base_executable);