mirror of
https://github.com/python/cpython.git
synced 2025-07-19 09:15:34 +00:00
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().
This commit is contained in:
parent
4662fa9bfe
commit
f3cb814315
6 changed files with 653 additions and 60 deletions
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue