diff options
author | Victor Stinner <vstinner@python.org> | 2024-09-02 21:25:08 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-02 21:25:08 (GMT) |
commit | 33b790978d8b817a66a4a117a8c38a857b6103f0 (patch) | |
tree | ea11817b7267715e2b19e944e42233c7a4a28399 /Python | |
parent | db42934270c5c23be9f6804cad98dfd8234caf6f (diff) | |
download | cpython-33b790978d8b817a66a4a117a8c38a857b6103f0.zip cpython-33b790978d8b817a66a4a117a8c38a857b6103f0.tar.gz cpython-33b790978d8b817a66a4a117a8c38a857b6103f0.tar.bz2 |
gh-107954, PEP 741: Add PyConfig_Get()/Set() functions (#123472)
Add PyConfig_Get(), PyConfig_GetInt(), PyConfig_Set() and
PyConfig_Names() functions to get and set the current runtime Python
configuration.
Add visibility and "sys spec" to config and preconfig specifications.
_PyConfig_AsDict() now converts PyConfig.xoptions as a dictionary.
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
Diffstat (limited to 'Python')
-rw-r--r-- | Python/initconfig.c | 894 | ||||
-rw-r--r-- | Python/sysmodule.c | 144 |
2 files changed, 844 insertions, 194 deletions
diff --git a/Python/initconfig.c b/Python/initconfig.c index 51897a2..1c21486 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -10,6 +10,7 @@ #include "pycore_pymem.h" // _PyMem_SetDefaultAllocator() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_pystats.h" // _Py_StatsOn() +#include "pycore_sysmodule.h" // _PySys_SetIntMaxStrDigits() #include "osdefs.h" // DELIM @@ -26,6 +27,22 @@ #include "config_common.h" +/* --- PyConfig setters ------------------------------------------- */ + +typedef PyObject* (*config_sys_flag_setter) (int value); + +static PyObject* +config_sys_flag_long(int value) +{ + return PyLong_FromLong(value); +} + +static PyObject* +config_sys_flag_not(int value) +{ + value = (!value); + return config_sys_flag_long(value); +} /* --- PyConfig spec ---------------------------------------------- */ @@ -40,99 +57,174 @@ typedef enum { PyConfig_MEMBER_WSTR_LIST = 12, } PyConfigMemberType; +typedef enum { + // Option which cannot be get or set by PyConfig_Get() and PyConfig_Set() + PyConfig_MEMBER_INIT_ONLY = 0, + + // Option which cannot be set by PyConfig_Set() + PyConfig_MEMBER_READ_ONLY = 1, + + // Public option: can be get and set by PyConfig_Get() and PyConfig_Set() + PyConfig_MEMBER_PUBLIC = 2, +} PyConfigMemberVisibility; + +typedef struct { + const char *attr; + int flag_index; + config_sys_flag_setter flag_setter; +} PyConfigSysSpec; + typedef struct { const char *name; size_t offset; PyConfigMemberType type; + PyConfigMemberVisibility visibility; + PyConfigSysSpec sys; } PyConfigSpec; -#define SPEC(MEMBER, TYPE) \ - {#MEMBER, offsetof(PyConfig, MEMBER), PyConfig_MEMBER_##TYPE} +#define SPEC(MEMBER, TYPE, VISIBILITY, sys) \ + {#MEMBER, offsetof(PyConfig, MEMBER), \ + PyConfig_MEMBER_##TYPE, PyConfig_MEMBER_##VISIBILITY, sys} + +#define SYS_ATTR(name) {name, -1, NULL} +#define SYS_FLAG_SETTER(index, setter) {NULL, index, setter} +#define SYS_FLAG(index) SYS_FLAG_SETTER(index, NULL) +#define NO_SYS SYS_ATTR(NULL) // Update _test_embed_set_config when adding new members static const PyConfigSpec PYCONFIG_SPEC[] = { - SPEC(_config_init, UINT), - SPEC(isolated, BOOL), - SPEC(use_environment, BOOL), - SPEC(dev_mode, BOOL), - SPEC(install_signal_handlers, BOOL), - SPEC(use_hash_seed, BOOL), - SPEC(hash_seed, ULONG), - SPEC(faulthandler, BOOL), - SPEC(tracemalloc, UINT), - SPEC(perf_profiling, UINT), - SPEC(import_time, BOOL), - SPEC(code_debug_ranges, BOOL), - SPEC(show_ref_count, BOOL), - SPEC(dump_refs, BOOL), - SPEC(dump_refs_file, WSTR_OPT), - SPEC(malloc_stats, BOOL), - SPEC(filesystem_encoding, WSTR), - SPEC(filesystem_errors, WSTR), - SPEC(pycache_prefix, WSTR_OPT), - SPEC(parse_argv, BOOL), - SPEC(orig_argv, WSTR_LIST), - SPEC(argv, WSTR_LIST), - SPEC(xoptions, WSTR_LIST), - SPEC(warnoptions, WSTR_LIST), - SPEC(site_import, BOOL), - SPEC(bytes_warning, UINT), - SPEC(warn_default_encoding, BOOL), - SPEC(inspect, BOOL), - SPEC(interactive, BOOL), - SPEC(optimization_level, UINT), - SPEC(parser_debug, BOOL), - SPEC(write_bytecode, BOOL), - SPEC(verbose, UINT), - SPEC(quiet, BOOL), - SPEC(user_site_directory, BOOL), - SPEC(configure_c_stdio, BOOL), - SPEC(buffered_stdio, BOOL), - SPEC(stdio_encoding, WSTR), - SPEC(stdio_errors, WSTR), -#ifdef MS_WINDOWS - SPEC(legacy_windows_stdio, BOOL), + // --- Public options ----------- + + SPEC(argv, WSTR_LIST, PUBLIC, SYS_ATTR("argv")), + SPEC(base_exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_exec_prefix")), + SPEC(base_executable, WSTR_OPT, PUBLIC, SYS_ATTR("_base_executable")), + SPEC(base_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("base_prefix")), + SPEC(bytes_warning, UINT, PUBLIC, SYS_FLAG(9)), + SPEC(exec_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("exec_prefix")), + SPEC(executable, WSTR_OPT, PUBLIC, SYS_ATTR("executable")), + SPEC(inspect, BOOL, PUBLIC, SYS_FLAG(1)), + SPEC(int_max_str_digits, UINT, PUBLIC, NO_SYS), + SPEC(interactive, BOOL, PUBLIC, SYS_FLAG(2)), + SPEC(module_search_paths, WSTR_LIST, PUBLIC, SYS_ATTR("path")), + SPEC(optimization_level, UINT, PUBLIC, SYS_FLAG(3)), + SPEC(parser_debug, BOOL, PUBLIC, SYS_FLAG(0)), + SPEC(platlibdir, WSTR, PUBLIC, SYS_ATTR("platlibdir")), + SPEC(prefix, WSTR_OPT, PUBLIC, SYS_ATTR("prefix")), + SPEC(pycache_prefix, WSTR_OPT, PUBLIC, SYS_ATTR("pycache_prefix")), + SPEC(quiet, BOOL, PUBLIC, SYS_FLAG(10)), + SPEC(stdlib_dir, WSTR_OPT, PUBLIC, SYS_ATTR("_stdlib_dir")), + SPEC(use_environment, BOOL, PUBLIC, SYS_FLAG_SETTER(7, config_sys_flag_not)), + SPEC(verbose, UINT, PUBLIC, SYS_FLAG(8)), + SPEC(warnoptions, WSTR_LIST, PUBLIC, SYS_ATTR("warnoptions")), + SPEC(write_bytecode, BOOL, PUBLIC, SYS_FLAG_SETTER(4, config_sys_flag_not)), + SPEC(xoptions, WSTR_LIST, PUBLIC, SYS_ATTR("_xoptions")), + + // --- Read-only options ----------- + +#ifdef Py_STATS + SPEC(_pystats, BOOL, READ_ONLY, NO_SYS), #endif - SPEC(check_hash_pycs_mode, WSTR), - SPEC(use_frozen_modules, BOOL), - SPEC(safe_path, BOOL), - SPEC(int_max_str_digits, INT), - SPEC(cpu_count, INT), + SPEC(buffered_stdio, BOOL, READ_ONLY, NO_SYS), + SPEC(check_hash_pycs_mode, WSTR, READ_ONLY, NO_SYS), + SPEC(code_debug_ranges, BOOL, READ_ONLY, NO_SYS), + SPEC(configure_c_stdio, BOOL, READ_ONLY, NO_SYS), + SPEC(cpu_count, INT, READ_ONLY, NO_SYS), + SPEC(dev_mode, BOOL, READ_ONLY, NO_SYS), // sys.flags.dev_mode + SPEC(dump_refs, BOOL, READ_ONLY, NO_SYS), + SPEC(dump_refs_file, WSTR_OPT, READ_ONLY, NO_SYS), #ifdef Py_GIL_DISABLED - SPEC(enable_gil, INT), + SPEC(enable_gil, INT, READ_ONLY, NO_SYS), #endif - SPEC(pathconfig_warnings, BOOL), - SPEC(program_name, WSTR), - SPEC(pythonpath_env, WSTR_OPT), - SPEC(home, WSTR_OPT), - SPEC(platlibdir, WSTR), - SPEC(sys_path_0, WSTR_OPT), - SPEC(module_search_paths_set, BOOL), - SPEC(module_search_paths, WSTR_LIST), - SPEC(stdlib_dir, WSTR_OPT), - SPEC(executable, WSTR_OPT), - SPEC(base_executable, WSTR_OPT), - SPEC(prefix, WSTR_OPT), - SPEC(base_prefix, WSTR_OPT), - SPEC(exec_prefix, WSTR_OPT), - SPEC(base_exec_prefix, WSTR_OPT), - SPEC(skip_source_first_line, BOOL), - SPEC(run_command, WSTR_OPT), - SPEC(run_module, WSTR_OPT), - SPEC(run_filename, WSTR_OPT), - SPEC(_install_importlib, BOOL), - SPEC(_init_main, BOOL), - SPEC(_is_python_build, BOOL), -#ifdef Py_STATS - SPEC(_pystats, BOOL), + SPEC(faulthandler, BOOL, READ_ONLY, NO_SYS), + SPEC(filesystem_encoding, WSTR, READ_ONLY, NO_SYS), + SPEC(filesystem_errors, WSTR, READ_ONLY, NO_SYS), + SPEC(hash_seed, ULONG, READ_ONLY, NO_SYS), + SPEC(home, WSTR_OPT, READ_ONLY, NO_SYS), + SPEC(import_time, BOOL, READ_ONLY, NO_SYS), + SPEC(install_signal_handlers, BOOL, READ_ONLY, NO_SYS), + SPEC(isolated, BOOL, READ_ONLY, NO_SYS), // sys.flags.isolated +#ifdef MS_WINDOWS + SPEC(legacy_windows_stdio, BOOL, READ_ONLY, NO_SYS), #endif + SPEC(malloc_stats, BOOL, READ_ONLY, NO_SYS), + SPEC(orig_argv, WSTR_LIST, READ_ONLY, SYS_ATTR("orig_argv")), + SPEC(parse_argv, BOOL, READ_ONLY, NO_SYS), + SPEC(pathconfig_warnings, BOOL, READ_ONLY, NO_SYS), + SPEC(perf_profiling, BOOL, READ_ONLY, NO_SYS), + SPEC(program_name, WSTR, READ_ONLY, NO_SYS), + SPEC(run_command, WSTR_OPT, READ_ONLY, NO_SYS), + SPEC(run_filename, WSTR_OPT, READ_ONLY, NO_SYS), + SPEC(run_module, WSTR_OPT, READ_ONLY, NO_SYS), #ifdef Py_DEBUG - SPEC(run_presite, WSTR_OPT), + SPEC(run_presite, WSTR_OPT, READ_ONLY, NO_SYS), +#endif + SPEC(safe_path, BOOL, READ_ONLY, NO_SYS), + SPEC(show_ref_count, BOOL, READ_ONLY, NO_SYS), + SPEC(site_import, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_site + SPEC(skip_source_first_line, BOOL, READ_ONLY, NO_SYS), + SPEC(stdio_encoding, WSTR, READ_ONLY, NO_SYS), + SPEC(stdio_errors, WSTR, READ_ONLY, NO_SYS), + SPEC(tracemalloc, UINT, READ_ONLY, NO_SYS), + SPEC(use_frozen_modules, BOOL, READ_ONLY, NO_SYS), + SPEC(use_hash_seed, BOOL, READ_ONLY, NO_SYS), + SPEC(user_site_directory, BOOL, READ_ONLY, NO_SYS), // sys.flags.no_user_site + SPEC(warn_default_encoding, BOOL, READ_ONLY, NO_SYS), + + // --- Init-only options ----------- + + SPEC(_config_init, UINT, INIT_ONLY, NO_SYS), + SPEC(_init_main, BOOL, INIT_ONLY, NO_SYS), + SPEC(_install_importlib, BOOL, INIT_ONLY, NO_SYS), + SPEC(_is_python_build, BOOL, INIT_ONLY, NO_SYS), + SPEC(module_search_paths_set, BOOL, INIT_ONLY, NO_SYS), + SPEC(pythonpath_env, WSTR_OPT, INIT_ONLY, NO_SYS), + SPEC(sys_path_0, WSTR_OPT, INIT_ONLY, NO_SYS), + + // Array terminator + {NULL, 0, 0, 0, NO_SYS}, +}; + +#undef SPEC +#define SPEC(MEMBER, TYPE, VISIBILITY) \ + {#MEMBER, offsetof(PyPreConfig, MEMBER), PyConfig_MEMBER_##TYPE, \ + PyConfig_MEMBER_##VISIBILITY, NO_SYS} + +static const PyConfigSpec PYPRECONFIG_SPEC[] = { + // --- Read-only options ----------- + + SPEC(allocator, INT, READ_ONLY), + SPEC(coerce_c_locale, BOOL, READ_ONLY), + SPEC(coerce_c_locale_warn, BOOL, READ_ONLY), + SPEC(configure_locale, BOOL, READ_ONLY), +#ifdef MS_WINDOWS + SPEC(legacy_windows_fs_encoding, BOOL, READ_ONLY), #endif - {NULL, 0, 0}, + SPEC(utf8_mode, BOOL, READ_ONLY), + + // --- Init-only options ----------- + // Members already present in PYCONFIG_SPEC + + SPEC(_config_init, INT, INIT_ONLY), + SPEC(dev_mode, BOOL, INIT_ONLY), + SPEC(isolated, BOOL, INIT_ONLY), + SPEC(parse_argv, BOOL, INIT_ONLY), + SPEC(use_environment, BOOL, INIT_ONLY), + + // Array terminator + {NULL, 0, 0, 0, NO_SYS}, }; #undef SPEC +#undef SYS_ATTR +#undef SYS_FLAG_SETTER +#undef SYS_FLAG +#undef NO_SYS + + +// Forward declarations +static PyObject* +config_get(const PyConfig *config, const PyConfigSpec *spec, + int use_sys); /* --- Command line options --------------------------------------- */ @@ -656,6 +748,28 @@ _PyWideStringList_AsList(const PyWideStringList *list) } +static PyObject* +_PyWideStringList_AsTuple(const PyWideStringList *list) +{ + assert(_PyWideStringList_CheckConsistency(list)); + + PyObject *tuple = PyTuple_New(list->length); + if (tuple == NULL) { + return NULL; + } + + for (Py_ssize_t i = 0; i < list->length; i++) { + PyObject *item = PyUnicode_FromWideChar(list->items[i], -1); + if (item == NULL) { + Py_DECREF(tuple); + return NULL; + } + PyTuple_SET_ITEM(tuple, i, item); + } + return tuple; +} + + /* --- Py_GetArgcArgv() ------------------------------------------- */ void @@ -1059,54 +1173,12 @@ _PyConfig_AsDict(const PyConfig *config) const PyConfigSpec *spec = PYCONFIG_SPEC; for (; spec->name != NULL; spec++) { - char *member = (char *)config + spec->offset; - PyObject *obj; - switch (spec->type) { - case PyConfig_MEMBER_INT: - case PyConfig_MEMBER_UINT: - { - int value = *(int*)member; - obj = PyLong_FromLong(value); - break; - } - case PyConfig_MEMBER_BOOL: - { - int value = *(int*)member; - obj = PyBool_FromLong(value); - break; - } - case PyConfig_MEMBER_ULONG: - { - unsigned long value = *(unsigned long*)member; - obj = PyLong_FromUnsignedLong(value); - break; - } - case PyConfig_MEMBER_WSTR: - case PyConfig_MEMBER_WSTR_OPT: - { - const wchar_t *wstr = *(const wchar_t**)member; - if (wstr != NULL) { - obj = PyUnicode_FromWideChar(wstr, -1); - } - else { - obj = Py_NewRef(Py_None); - } - break; - } - case PyConfig_MEMBER_WSTR_LIST: - { - const PyWideStringList *list = (const PyWideStringList*)member; - obj = _PyWideStringList_AsList(list); - break; - } - default: - Py_UNREACHABLE(); - } - + PyObject *obj = config_get(config, spec, 0); if (obj == NULL) { Py_DECREF(dict); return NULL; } + int res = PyDict_SetItemString(dict, spec->name, obj); Py_DECREF(obj); if (res < 0) { @@ -1218,15 +1290,17 @@ config_dict_get_wstrlist(PyObject *dict, const char *name, PyConfig *config, return -1; } - if (!PyList_CheckExact(list)) { + int is_list = PyList_CheckExact(list); + if (!is_list && !PyTuple_CheckExact(list)) { Py_DECREF(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); + Py_ssize_t len = is_list ? PyList_GET_SIZE(list) : PyTuple_GET_SIZE(list); + for (Py_ssize_t i=0; i < len; i++) { + PyObject *item = is_list ? PyList_GET_ITEM(list, i) : PyTuple_GET_ITEM(list, i); if (item == Py_None) { config_dict_invalid_value(name); @@ -1263,6 +1337,66 @@ error: } +static int +config_dict_get_xoptions(PyObject *dict, const char *name, PyConfig *config, + PyWideStringList *result) +{ + PyObject *xoptions = config_dict_get(dict, name); + if (xoptions == NULL) { + return -1; + } + + if (!PyDict_CheckExact(xoptions)) { + Py_DECREF(xoptions); + config_dict_invalid_type(name); + return -1; + } + + Py_ssize_t pos = 0; + PyObject *key, *value; + PyWideStringList wstrlist = _PyWideStringList_INIT; + while (PyDict_Next(xoptions, &pos, &key, &value)) { + PyObject *item; + + if (value != Py_True) { + item = PyUnicode_FromFormat("%S=%S", key, value); + if (item == NULL) { + goto error; + } + } + else { + item = Py_NewRef(key); + } + + wchar_t *wstr = PyUnicode_AsWideCharString(item, NULL); + Py_DECREF(item); + 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); + Py_DECREF(xoptions); + return 0; + +error: + _PyWideStringList_Clear(&wstrlist); + Py_DECREF(xoptions); + return -1; +} + + int _PyConfig_FromDict(PyConfig *config, PyObject *dict) { @@ -1324,9 +1458,17 @@ _PyConfig_FromDict(PyConfig *config, PyObject *dict) } case PyConfig_MEMBER_WSTR_LIST: { - if (config_dict_get_wstrlist(dict, spec->name, config, - (PyWideStringList*)member) < 0) { - return -1; + if (strcmp(spec->name, "xoptions") == 0) { + if (config_dict_get_xoptions(dict, spec->name, config, + (PyWideStringList*)member) < 0) { + return -1; + } + } + else { + if (config_dict_get_wstrlist(dict, spec->name, config, + (PyWideStringList*)member) < 0) { + return -1; + } } break; } @@ -3261,3 +3403,505 @@ _Py_DumpPathConfig(PyThreadState *tstate) _PyErr_SetRaisedException(tstate, exc); } + + +// --- PyConfig_Get() ------------------------------------------------------- + +static void* +config_spec_get_member(const PyConfigSpec *spec, const PyConfig *config) +{ + return (char *)config + spec->offset; +} + + +static const PyConfigSpec* +config_generic_find_spec(const PyConfigSpec *spec, const char *name) +{ + for (; spec->name != NULL; spec++) { + if (spec->visibility == PyConfig_MEMBER_INIT_ONLY) { + continue; + } + if (strcmp(name, spec->name) == 0) { + return spec; + } + } + return NULL; +} + + +static const PyConfigSpec* +config_find_spec(const char *name) +{ + return config_generic_find_spec(PYCONFIG_SPEC, name); +} + + +static const PyConfigSpec* +preconfig_find_spec(const char *name) +{ + return config_generic_find_spec(PYPRECONFIG_SPEC, name); +} + + +static int +config_add_xoption(PyObject *dict, const wchar_t *str) +{ + PyObject *name = NULL, *value = NULL; + + const wchar_t *name_end = wcschr(str, L'='); + if (!name_end) { + name = PyUnicode_FromWideChar(str, -1); + if (name == NULL) { + goto error; + } + value = Py_NewRef(Py_True); + } + else { + name = PyUnicode_FromWideChar(str, name_end - str); + if (name == NULL) { + goto error; + } + value = PyUnicode_FromWideChar(name_end + 1, -1); + if (value == NULL) { + goto error; + } + } + if (PyDict_SetItem(dict, name, value) < 0) { + goto error; + } + Py_DECREF(name); + Py_DECREF(value); + return 0; + +error: + Py_XDECREF(name); + Py_XDECREF(value); + return -1; +} + + +PyObject* +_PyConfig_CreateXOptionsDict(const PyConfig *config) +{ + PyObject *dict = PyDict_New(); + if (dict == NULL) { + return NULL; + } + + Py_ssize_t nxoption = config->xoptions.length; + wchar_t **xoptions = config->xoptions.items; + for (Py_ssize_t i=0; i < nxoption; i++) { + const wchar_t *option = xoptions[i]; + if (config_add_xoption(dict, option) < 0) { + Py_DECREF(dict); + return NULL; + } + } + return dict; +} + + +static PyObject* +config_get_sys(const char *name) +{ + PyObject *value = PySys_GetObject(name); + if (value == NULL) { + PyErr_Format(PyExc_RuntimeError, "lost sys.%s", name); + return NULL; + } + return Py_NewRef(value); +} + + +static int +config_get_sys_write_bytecode(const PyConfig *config, int *value) +{ + PyObject *attr = config_get_sys("dont_write_bytecode"); + if (attr == NULL) { + return -1; + } + + int is_true = PyObject_IsTrue(attr); + Py_DECREF(attr); + if (is_true < 0) { + return -1; + } + *value = (!is_true); + return 0; +} + + +static PyObject* +config_get(const PyConfig *config, const PyConfigSpec *spec, + int use_sys) +{ + if (use_sys) { + if (spec->sys.attr != NULL) { + return config_get_sys(spec->sys.attr); + } + + if (strcmp(spec->name, "write_bytecode") == 0) { + int value; + if (config_get_sys_write_bytecode(config, &value) < 0) { + return NULL; + } + return PyBool_FromLong(value); + } + + if (strcmp(spec->name, "int_max_str_digits") == 0) { + PyInterpreterState *interp = _PyInterpreterState_GET(); + return PyLong_FromLong(interp->long_state.max_str_digits); + } + } + + void *member = config_spec_get_member(spec, config); + switch (spec->type) { + case PyConfig_MEMBER_INT: + case PyConfig_MEMBER_UINT: + { + int value = *(int *)member; + return PyLong_FromLong(value); + } + + case PyConfig_MEMBER_BOOL: + { + int value = *(int *)member; + return PyBool_FromLong(value != 0); + } + + case PyConfig_MEMBER_ULONG: + { + unsigned long value = *(unsigned long *)member; + return PyLong_FromUnsignedLong(value); + } + + case PyConfig_MEMBER_WSTR: + case PyConfig_MEMBER_WSTR_OPT: + { + wchar_t *wstr = *(wchar_t **)member; + if (wstr != NULL) { + return PyUnicode_FromWideChar(wstr, -1); + } + else { + return Py_NewRef(Py_None); + } + } + + case PyConfig_MEMBER_WSTR_LIST: + { + if (strcmp(spec->name, "xoptions") == 0) { + return _PyConfig_CreateXOptionsDict(config); + } + else { + const PyWideStringList *list = (const PyWideStringList *)member; + return _PyWideStringList_AsTuple(list); + } + } + + default: + Py_UNREACHABLE(); + } +} + + +static PyObject* +preconfig_get(const PyPreConfig *preconfig, const PyConfigSpec *spec) +{ + // The type of all PYPRECONFIG_SPEC members is INT or BOOL. + assert(spec->type == PyConfig_MEMBER_INT + || spec->type == PyConfig_MEMBER_BOOL); + + char *member = (char *)preconfig + spec->offset; + int value = *(int *)member; + + if (spec->type == PyConfig_MEMBER_BOOL) { + return PyBool_FromLong(value != 0); + } + else { + return PyLong_FromLong(value); + } +} + + +static void +config_unknown_name_error(const char *name) +{ + PyErr_Format(PyExc_ValueError, "unknown config option name: %s", name); +} + + +PyObject* +PyConfig_Get(const char *name) +{ + const PyConfigSpec *spec = config_find_spec(name); + if (spec != NULL) { + const PyConfig *config = _Py_GetConfig(); + return config_get(config, spec, 1); + } + + spec = preconfig_find_spec(name); + if (spec != NULL) { + const PyPreConfig *preconfig = &_PyRuntime.preconfig; + return preconfig_get(preconfig, spec); + } + + config_unknown_name_error(name); + return NULL; +} + + +int +PyConfig_GetInt(const char *name, int *value) +{ + assert(!PyErr_Occurred()); + + PyObject *obj = PyConfig_Get(name); + if (obj == NULL) { + return -1; + } + + if (!PyLong_Check(obj)) { + Py_DECREF(obj); + PyErr_Format(PyExc_TypeError, "config option %s is not an int", name); + return -1; + } + + int as_int = PyLong_AsInt(obj); + Py_DECREF(obj); + if (as_int == -1 && PyErr_Occurred()) { + PyErr_Format(PyExc_OverflowError, + "config option %s value does not fit into a C int", name); + return -1; + } + + *value = as_int; + return 0; +} + + +static int +config_names_add(PyObject *names, const PyConfigSpec *spec) +{ + for (; spec->name != NULL; spec++) { + if (spec->visibility == PyConfig_MEMBER_INIT_ONLY) { + continue; + } + PyObject *name = PyUnicode_FromString(spec->name); + if (name == NULL) { + return -1; + } + int res = PyList_Append(names, name); + Py_DECREF(name); + if (res < 0) { + return -1; + } + } + return 0; +} + + +PyObject* +PyConfig_Names(void) +{ + PyObject *names = PyList_New(0); + if (names == NULL) { + goto error; + } + + if (config_names_add(names, PYCONFIG_SPEC) < 0) { + goto error; + } + if (config_names_add(names, PYPRECONFIG_SPEC) < 0) { + goto error; + } + + PyObject *frozen = PyFrozenSet_New(names); + Py_DECREF(names); + return frozen; + +error: + Py_XDECREF(names); + return NULL; +} + + +// --- PyConfig_Set() ------------------------------------------------------- + +static int +config_set_sys_flag(const PyConfigSpec *spec, int int_value) +{ + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyConfig *config = &interp->config; + + if (spec->type == PyConfig_MEMBER_BOOL) { + if (int_value != 0) { + // convert values < 0 and values > 1 to 1 + int_value = 1; + } + } + + PyObject *value; + if (spec->sys.flag_setter) { + value = spec->sys.flag_setter(int_value); + } + else { + value = config_sys_flag_long(int_value); + } + if (value == NULL) { + return -1; + } + + // Set sys.flags.FLAG + Py_ssize_t pos = spec->sys.flag_index; + if (_PySys_SetFlagObj(pos, value) < 0) { + goto error; + } + + // Set PyConfig.ATTR + assert(spec->type == PyConfig_MEMBER_INT + || spec->type == PyConfig_MEMBER_UINT + || spec->type == PyConfig_MEMBER_BOOL); + int *member = config_spec_get_member(spec, config); + *member = int_value; + + // Set sys.dont_write_bytecode attribute + if (strcmp(spec->name, "write_bytecode") == 0) { + if (PySys_SetObject("dont_write_bytecode", value) < 0) { + goto error; + } + } + + Py_DECREF(value); + return 0; + +error: + Py_DECREF(value); + return -1; +} + + +int +PyConfig_Set(const char *name, PyObject *value) +{ + const PyConfigSpec *spec = config_find_spec(name); + if (spec == NULL) { + spec = preconfig_find_spec(name); + if (spec == NULL) { + config_unknown_name_error(name); + return -1; + } + assert(spec->visibility != PyConfig_MEMBER_PUBLIC); + } + + if (spec->visibility != PyConfig_MEMBER_PUBLIC) { + PyErr_Format(PyExc_ValueError, "cannot set read-only option %s", + name); + return -1; + } + + int int_value = 0; + int has_int_value = 0; + + switch (spec->type) { + case PyConfig_MEMBER_INT: + case PyConfig_MEMBER_UINT: + case PyConfig_MEMBER_BOOL: + if (!PyLong_Check(value)) { + PyErr_Format(PyExc_TypeError, "expected int or bool, got %T", value); + return -1; + } + int_value = PyLong_AsInt(value); + if (int_value == -1 && PyErr_Occurred()) { + return -1; + } + if (int_value < 0 && spec->type != PyConfig_MEMBER_INT) { + PyErr_Format(PyExc_ValueError, "value must be >= 0"); + return -1; + } + has_int_value = 1; + break; + + case PyConfig_MEMBER_ULONG: + // not implemented: only hash_seed uses this type, and it's read-only + goto cannot_set; + + case PyConfig_MEMBER_WSTR: + if (!PyUnicode_CheckExact(value)) { + PyErr_Format(PyExc_TypeError, "expected str, got %T", value); + return -1; + } + break; + + case PyConfig_MEMBER_WSTR_OPT: + if (value != Py_None && !PyUnicode_CheckExact(value)) { + PyErr_Format(PyExc_TypeError, "expected str or None, got %T", value); + return -1; + } + break; + + case PyConfig_MEMBER_WSTR_LIST: + if (strcmp(spec->name, "xoptions") != 0) { + if (!PyList_Check(value)) { + PyErr_Format(PyExc_TypeError, "expected list[str], got %T", + value); + return -1; + } + for (Py_ssize_t i=0; i < PyList_GET_SIZE(value); i++) { + PyObject *item = PyList_GET_ITEM(value, i); + if (!PyUnicode_Check(item)) { + PyErr_Format(PyExc_TypeError, + "expected str, list item %zd has type %T", + i, item); + return -1; + } + } + } + else { + // xoptions type is dict[str, str] + if (!PyDict_Check(value)) { + PyErr_Format(PyExc_TypeError, + "expected dict[str, str | bool], got %T", + value); + return -1; + } + + Py_ssize_t pos = 0; + PyObject *key, *item; + while (PyDict_Next(value, &pos, &key, &item)) { + if (!PyUnicode_Check(key)) { + PyErr_Format(PyExc_TypeError, + "expected str, " + "got dict key type %T", key); + return -1; + } + if (!PyUnicode_Check(item) && !PyBool_Check(item)) { + PyErr_Format(PyExc_TypeError, + "expected str or bool, " + "got dict value type %T", key); + return -1; + } + } + } + break; + + default: + Py_UNREACHABLE(); + } + + + if (spec->sys.attr != NULL) { + // Set the sys attribute, but don't set PyInterpreterState.config + // to keep the code simple. + return PySys_SetObject(spec->sys.attr, value); + } + else if (spec->sys.flag_index >= 0 && has_int_value) { + return config_set_sys_flag(spec, int_value); + } + else if (strcmp(spec->name, "int_max_str_digits") == 0 && has_int_value) { + return _PySys_SetIntMaxStrDigits(int_value); + } + +cannot_set: + PyErr_Format(PyExc_ValueError, "cannot set option %s", name); + return -1; +} diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 1fff7e4..887a916 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -1845,6 +1845,7 @@ sys_get_int_max_str_digits_impl(PyObject *module) return PyLong_FromLong(interp->long_state.max_str_digits); } + /*[clinic input] sys.set_int_max_str_digits @@ -1857,16 +1858,10 @@ static PyObject * sys_set_int_max_str_digits_impl(PyObject *module, int maxdigits) /*[clinic end generated code: output=734d4c2511f2a56d input=d7e3f325db6910c5]*/ { - PyThreadState *tstate = _PyThreadState_GET(); - if ((!maxdigits) || (maxdigits >= _PY_LONG_MAX_STR_DIGITS_THRESHOLD)) { - tstate->interp->long_state.max_str_digits = maxdigits; - Py_RETURN_NONE; - } else { - PyErr_Format( - PyExc_ValueError, "maxdigits must be 0 or larger than %d", - _PY_LONG_MAX_STR_DIGITS_THRESHOLD); + if (_PySys_SetIntMaxStrDigits(maxdigits) < 0) { return NULL; } + Py_RETURN_NONE; } size_t @@ -3120,6 +3115,8 @@ static PyStructSequence_Field flags_fields[] = { {0} }; +#define SYS_FLAGS_INT_MAX_STR_DIGITS 17 + static PyStructSequence_Desc flags_desc = { "sys.flags", /* name */ flags__doc__, /* doc */ @@ -3127,6 +3124,48 @@ static PyStructSequence_Desc flags_desc = { 18 }; +static void +sys_set_flag(PyObject *flags, Py_ssize_t pos, PyObject *value) +{ + assert(pos >= 0 && pos < (Py_ssize_t)(Py_ARRAY_LENGTH(flags_fields) - 1)); + + PyObject *old_value = PyStructSequence_GET_ITEM(flags, pos); + PyStructSequence_SET_ITEM(flags, pos, Py_NewRef(value)); + Py_XDECREF(old_value); +} + + +int +_PySys_SetFlagObj(Py_ssize_t pos, PyObject *value) +{ + PyObject *flags = Py_XNewRef(PySys_GetObject("flags")); + if (flags == NULL) { + if (!PyErr_Occurred()) { + PyErr_SetString(PyExc_RuntimeError, "lost sys.flags"); + } + return -1; + } + + sys_set_flag(flags, pos, value); + Py_DECREF(flags); + return 0; +} + + +static int +_PySys_SetFlagInt(Py_ssize_t pos, int value) +{ + PyObject *obj = PyLong_FromLong(value); + if (obj == NULL) { + return -1; + } + + int res = _PySys_SetFlagObj(pos, obj); + Py_DECREF(obj); + return res; +} + + static int set_flags_from_config(PyInterpreterState *interp, PyObject *flags) { @@ -3142,8 +3181,8 @@ set_flags_from_config(PyInterpreterState *interp, PyObject *flags) if (value == NULL) { \ return -1; \ } \ - Py_XDECREF(PyStructSequence_GET_ITEM(flags, pos)); \ - PyStructSequence_SET_ITEM(flags, pos, value); \ + sys_set_flag(flags, pos, value); \ + Py_DECREF(value); \ pos++; \ } while (0) #define SetFlag(expr) SetFlagObj(PyLong_FromLong(expr)) @@ -3599,64 +3638,6 @@ err_occurred: return _PyStatus_ERR("can't initialize sys module"); } -static int -sys_add_xoption(PyObject *opts, const wchar_t *s) -{ - PyObject *name, *value = NULL; - - const wchar_t *name_end = wcschr(s, L'='); - if (!name_end) { - name = PyUnicode_FromWideChar(s, -1); - if (name == NULL) { - goto error; - } - value = Py_NewRef(Py_True); - } - else { - name = PyUnicode_FromWideChar(s, name_end - s); - if (name == NULL) { - goto error; - } - value = PyUnicode_FromWideChar(name_end + 1, -1); - if (value == NULL) { - goto error; - } - } - if (PyDict_SetItem(opts, name, value) < 0) { - goto error; - } - Py_DECREF(name); - Py_DECREF(value); - return 0; - -error: - Py_XDECREF(name); - Py_XDECREF(value); - return -1; -} - - -static PyObject* -sys_create_xoptions_dict(const PyConfig *config) -{ - Py_ssize_t nxoption = config->xoptions.length; - wchar_t * const * xoptions = config->xoptions.items; - PyObject *dict = PyDict_New(); - if (dict == NULL) { - return NULL; - } - - for (Py_ssize_t i=0; i < nxoption; i++) { - const wchar_t *option = xoptions[i]; - if (sys_add_xoption(dict, option) < 0) { - Py_DECREF(dict); - return NULL; - } - } - - return dict; -} - // Update sys attributes for a new PyConfig configuration. // This function also adds attributes that _PySys_InitCore() didn't add. @@ -3703,7 +3684,7 @@ _PySys_UpdateConfig(PyThreadState *tstate) COPY_LIST("orig_argv", config->orig_argv); COPY_LIST("warnoptions", config->warnoptions); - SET_SYS("_xoptions", sys_create_xoptions_dict(config)); + SET_SYS("_xoptions", _PyConfig_CreateXOptionsDict(config)); const wchar_t *stdlibdir = _Py_GetStdlibDir(); if (stdlibdir != NULL) { @@ -4129,3 +4110,28 @@ PySys_FormatStderr(const char *format, ...) sys_format(&_Py_ID(stderr), stderr, format, va); va_end(va); } + + +int +_PySys_SetIntMaxStrDigits(int maxdigits) +{ + if (maxdigits != 0 && maxdigits < _PY_LONG_MAX_STR_DIGITS_THRESHOLD) { + PyErr_Format( + PyExc_ValueError, "maxdigits must be 0 or larger than %d", + _PY_LONG_MAX_STR_DIGITS_THRESHOLD); + return -1; + } + + // Set sys.flags.int_max_str_digits + const Py_ssize_t pos = SYS_FLAGS_INT_MAX_STR_DIGITS; + if (_PySys_SetFlagInt(pos, maxdigits) < 0) { + return -1; + } + + // Set PyInterpreterState.long_state.max_str_digits + // and PyInterpreterState.config.int_max_str_digits. + PyInterpreterState *interp = _PyInterpreterState_GET(); + interp->long_state.max_str_digits = maxdigits; + interp->config.int_max_str_digits = maxdigits; + return 0; +} |