diff options
author | Victor Stinner <vstinner@redhat.com> | 2019-09-29 23:40:17 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-09-29 23:40:17 (GMT) |
commit | fb4ae152a9930f0e00cae8b2807f534058cf341a (patch) | |
tree | 557401018629793ae3de044ca7024384a0e4f3fa /Python/initconfig.c | |
parent | 58498bc7178608b1ab031994ca09c43889ce3e76 (diff) | |
download | cpython-fb4ae152a9930f0e00cae8b2807f534058cf341a.zip cpython-fb4ae152a9930f0e00cae8b2807f534058cf341a.tar.gz cpython-fb4ae152a9930f0e00cae8b2807f534058cf341a.tar.bz2 |
bpo-38317: Fix PyConfig.warnoptions priority (GH-16478)
Fix warnings options priority: PyConfig.warnoptions has the highest
priority, as stated in the PEP 587.
* Document options order in PyConfig.warnoptions documentation.
* Make PyWideStringList_INIT macro private: replace "Py" prefix
with "_Py".
* test_embed: add test_init_warnoptions().
Diffstat (limited to 'Python/initconfig.c')
-rw-r--r-- | Python/initconfig.c | 124 |
1 files changed, 82 insertions, 42 deletions
diff --git a/Python/initconfig.c b/Python/initconfig.c index 62c868d..dec4bf2 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -273,7 +273,7 @@ _PyWideStringList_Copy(PyWideStringList *list, const PyWideStringList *list2) return 0; } - PyWideStringList copy = PyWideStringList_INIT; + PyWideStringList copy = _PyWideStringList_INIT; size_t size = list2->length * sizeof(list2->items[0]); copy.items = PyMem_RawMalloc(size); @@ -2095,63 +2095,83 @@ config_init_env_warnoptions(PyConfig *config, PyWideStringList *warnoptions) static PyStatus -config_add_warnoption(PyConfig *config, const wchar_t *option) +warnoptions_append(PyConfig *config, PyWideStringList *options, + const wchar_t *option) { + /* config_init_warnoptions() add existing config warnoptions at the end: + ensure that the new option is not already present in this list to + prevent change the options order whne config_init_warnoptions() is + called twice. */ if (_PyWideStringList_Find(&config->warnoptions, option)) { /* Already present: do nothing */ return _PyStatus_OK(); } - return PyWideStringList_Append(&config->warnoptions, option); + if (_PyWideStringList_Find(options, option)) { + /* Already present: do nothing */ + return _PyStatus_OK(); + } + return PyWideStringList_Append(options, option); +} + + +static PyStatus +warnoptions_extend(PyConfig *config, PyWideStringList *options, + const PyWideStringList *options2) +{ + const Py_ssize_t len = options2->length; + wchar_t *const *items = options2->items; + + for (Py_ssize_t i = 0; i < len; i++) { + PyStatus status = warnoptions_append(config, options, items[i]); + if (_PyStatus_EXCEPTION(status)) { + return status; + } + } + return _PyStatus_OK(); } static PyStatus config_init_warnoptions(PyConfig *config, const PyWideStringList *cmdline_warnoptions, - const PyWideStringList *env_warnoptions) + const PyWideStringList *env_warnoptions, + const PyWideStringList *sys_warnoptions) { PyStatus status; + PyWideStringList options = _PyWideStringList_INIT; - /* The priority order for warnings configuration is (highest precedence - * first): + /* Priority of warnings options, lowest to highest: * - * - early PySys_AddWarnOption() calls - * - the BytesWarning filter, if needed ('-b', '-bb') - * - any '-W' command line options; then - * - the 'PYTHONWARNINGS' environment variable; then - * - the dev mode filter ('-X dev', 'PYTHONDEVMODE'); then * - any implicit filters added by _warnings.c/warnings.py + * - PyConfig.dev_mode: "default" filter + * - PYTHONWARNINGS environment variable + * - '-W' command line options + * - PyConfig.bytes_warning ('-b' and '-bb' command line options): + * "default::BytesWarning" or "error::BytesWarning" filter + * - early PySys_AddWarnOption() calls + * - PyConfig.warnoptions * - * All settings except the last are passed to the warnings module via - * the `sys.warnoptions` list. Since the warnings module works on the basis - * of "the most recently added filter will be checked first", we add - * the lowest precedence entries first so that later entries override them. + * PyConfig.warnoptions is copied to sys.warnoptions. Since the warnings + * module works on the basis of "the most recently added filter will be + * checked first", we add the lowest precedence entries first so that later + * entries override them. */ if (config->dev_mode) { - status = config_add_warnoption(config, L"default"); + status = warnoptions_append(config, &options, L"default"); if (_PyStatus_EXCEPTION(status)) { - return status; + goto error; } } - Py_ssize_t i; - const PyWideStringList *options; - - options = env_warnoptions; - for (i = 0; i < options->length; i++) { - status = config_add_warnoption(config, options->items[i]); - if (_PyStatus_EXCEPTION(status)) { - return status; - } + status = warnoptions_extend(config, &options, env_warnoptions); + if (_PyStatus_EXCEPTION(status)) { + goto error; } - options = cmdline_warnoptions; - for (i = 0; i < options->length; i++) { - status = config_add_warnoption(config, options->items[i]); - if (_PyStatus_EXCEPTION(status)) { - return status; - } + status = warnoptions_extend(config, &options, cmdline_warnoptions); + if (_PyStatus_EXCEPTION(status)) { + goto error; } /* If the bytes_warning_flag isn't set, bytesobject.c and bytearrayobject.c @@ -2166,19 +2186,30 @@ config_init_warnoptions(PyConfig *config, else { filter = L"default::BytesWarning"; } - status = config_add_warnoption(config, filter); + status = warnoptions_append(config, &options, filter); if (_PyStatus_EXCEPTION(status)) { - return status; + goto error; } } - /* Handle early PySys_AddWarnOption() calls */ - status = _PySys_ReadPreinitWarnOptions(config); + status = warnoptions_extend(config, &options, sys_warnoptions); if (_PyStatus_EXCEPTION(status)) { - return status; + goto error; + } + + /* Always add all PyConfig.warnoptions options */ + status = _PyWideStringList_Extend(&options, &config->warnoptions); + if (_PyStatus_EXCEPTION(status)) { + goto error; } + _PyWideStringList_Clear(&config->warnoptions); + config->warnoptions = options; return _PyStatus_OK(); + +error: + _PyWideStringList_Clear(&options); + return status; } @@ -2186,7 +2217,7 @@ static PyStatus config_update_argv(PyConfig *config, Py_ssize_t opt_index) { const PyWideStringList *cmdline_argv = &config->argv; - PyWideStringList config_argv = PyWideStringList_INIT; + PyWideStringList config_argv = _PyWideStringList_INIT; /* Copy argv to be able to modify it (to force -c/-m) */ if (cmdline_argv->length <= opt_index) { @@ -2306,8 +2337,9 @@ static PyStatus config_read_cmdline(PyConfig *config) { PyStatus status; - PyWideStringList cmdline_warnoptions = PyWideStringList_INIT; - PyWideStringList env_warnoptions = PyWideStringList_INIT; + PyWideStringList cmdline_warnoptions = _PyWideStringList_INIT; + PyWideStringList env_warnoptions = _PyWideStringList_INIT; + PyWideStringList sys_warnoptions = _PyWideStringList_INIT; if (config->parse_argv < 0) { config->parse_argv = 1; @@ -2351,9 +2383,16 @@ config_read_cmdline(PyConfig *config) } } + /* Handle early PySys_AddWarnOption() calls */ + status = _PySys_ReadPreinitWarnOptions(&sys_warnoptions); + if (_PyStatus_EXCEPTION(status)) { + goto done; + } + status = config_init_warnoptions(config, &cmdline_warnoptions, - &env_warnoptions); + &env_warnoptions, + &sys_warnoptions); if (_PyStatus_EXCEPTION(status)) { goto done; } @@ -2363,6 +2402,7 @@ config_read_cmdline(PyConfig *config) done: _PyWideStringList_Clear(&cmdline_warnoptions); _PyWideStringList_Clear(&env_warnoptions); + _PyWideStringList_Clear(&sys_warnoptions); return status; } @@ -2433,7 +2473,7 @@ PyStatus PyConfig_Read(PyConfig *config) { PyStatus status; - PyWideStringList orig_argv = PyWideStringList_INIT; + PyWideStringList orig_argv = _PyWideStringList_INIT; status = config_check_struct_size(config); if (_PyStatus_EXCEPTION(status)) { |