bpo-36763: Fix _PyPreConfig_InitCompatConfig() utf8_mode (GH-13518)

* _PyPreConfig_InitCompatConfig() sets utf8_mode to 0.
* Change Py_UTF8Mode default value to 0.
* Fix _PyPreConfig_Copy(): copy also _config_init attrbibute.
* _PyPreConfig_AsDict() exports _config_init
* Fix _PyPreConfig_GetGlobalConfig(): use Py_UTF8Mode if it's greater
  than 0, even if utf8_mode >= 0.
* Add unit tests on environment variables using Python API.
This commit is contained in:
Victor Stinner 2019-05-23 04:12:27 +02:00 committed by GitHub
parent bc2aa81662
commit 20e1e2582e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 77 additions and 11 deletions

View File

@ -287,6 +287,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
IGNORE_CONFIG = object() IGNORE_CONFIG = object()
PRE_CONFIG_COMPAT = { PRE_CONFIG_COMPAT = {
'_config_init': API_COMPAT,
'allocator': PYMEM_ALLOCATOR_NOT_SET, 'allocator': PYMEM_ALLOCATOR_NOT_SET,
'parse_argv': 0, 'parse_argv': 0,
'configure_locale': 1, 'configure_locale': 1,
@ -299,11 +300,13 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'legacy_windows_fs_encoding': 0, 'legacy_windows_fs_encoding': 0,
}) })
PRE_CONFIG_PYTHON = dict(PRE_CONFIG_COMPAT, PRE_CONFIG_PYTHON = dict(PRE_CONFIG_COMPAT,
_config_init=API_PYTHON,
parse_argv=1, parse_argv=1,
coerce_c_locale=GET_DEFAULT_CONFIG, coerce_c_locale=GET_DEFAULT_CONFIG,
utf8_mode=GET_DEFAULT_CONFIG, utf8_mode=GET_DEFAULT_CONFIG,
) )
PRE_CONFIG_ISOLATED = dict(PRE_CONFIG_COMPAT, PRE_CONFIG_ISOLATED = dict(PRE_CONFIG_COMPAT,
_config_init=API_ISOLATED,
configure_locale=0, configure_locale=0,
isolated=1, isolated=1,
use_environment=0, use_environment=0,
@ -388,10 +391,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
}) })
CORE_CONFIG_PYTHON = dict(CORE_CONFIG_COMPAT, CORE_CONFIG_PYTHON = dict(CORE_CONFIG_COMPAT,
_config_init=API_PYTHON,
configure_c_stdio=1, configure_c_stdio=1,
parse_argv=1, parse_argv=1,
) )
CORE_CONFIG_ISOLATED = dict(CORE_CONFIG_COMPAT, CORE_CONFIG_ISOLATED = dict(CORE_CONFIG_COMPAT,
_config_init=API_ISOLATED,
isolated=1, isolated=1,
use_environment=0, use_environment=0,
user_site_directory=0, user_site_directory=0,
@ -611,7 +616,6 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
else: else:
default_config = self.CORE_CONFIG_COMPAT default_config = self.CORE_CONFIG_COMPAT
expected_config = dict(default_config, **expected_config) expected_config = dict(default_config, **expected_config)
expected_config['_config_init'] = api
self.get_expected_config(expected_preconfig, self.get_expected_config(expected_preconfig,
expected_config, env, expected_config, env,
@ -708,7 +712,33 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
self.check_config("test_init_from_config", config, preconfig, self.check_config("test_init_from_config", config, preconfig,
api=API_COMPAT) api=API_COMPAT)
def test_init_env(self): def test_init_compat_env(self):
preconfig = {
'allocator': PYMEM_ALLOCATOR_MALLOC,
}
config = {
'use_hash_seed': 1,
'hash_seed': 42,
'tracemalloc': 2,
'import_time': 1,
'malloc_stats': 1,
'inspect': 1,
'optimization_level': 2,
'module_search_path_env': '/my/path',
'pycache_prefix': 'env_pycache_prefix',
'write_bytecode': 0,
'verbose': 1,
'buffered_stdio': 0,
'stdio_encoding': 'iso8859-1',
'stdio_errors': 'replace',
'user_site_directory': 0,
'faulthandler': 1,
'warnoptions': ['EnvVar'],
}
self.check_config("test_init_compat_env", config, preconfig,
api=API_COMPAT)
def test_init_python_env(self):
preconfig = { preconfig = {
'allocator': PYMEM_ALLOCATOR_MALLOC, 'allocator': PYMEM_ALLOCATOR_MALLOC,
'utf8_mode': 1, 'utf8_mode': 1,
@ -732,8 +762,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'faulthandler': 1, 'faulthandler': 1,
'warnoptions': ['EnvVar'], 'warnoptions': ['EnvVar'],
} }
self.check_config("test_init_env", config, preconfig, self.check_config("test_init_python_env", config, preconfig,
api=API_COMPAT) api=API_PYTHON)
def test_init_env_dev_mode(self): def test_init_env_dev_mode(self):
preconfig = dict(allocator=PYMEM_ALLOCATOR_DEBUG) preconfig = dict(allocator=PYMEM_ALLOCATOR_DEBUG)

View File

@ -638,7 +638,7 @@ static void set_all_env_vars(void)
} }
static int test_init_env(void) static int test_init_compat_env(void)
{ {
/* Test initialization from environment variables */ /* Test initialization from environment variables */
Py_IgnoreEnvironmentFlag = 0; Py_IgnoreEnvironmentFlag = 0;
@ -650,6 +650,29 @@ static int test_init_env(void)
} }
static int test_init_python_env(void)
{
_PyInitError err;
set_all_env_vars();
_PyCoreConfig config;
err = _PyCoreConfig_InitPythonConfig(&config);
if (_PyInitError_Failed(err)) {
_Py_ExitInitError(err);
}
config.program_name = L"./_testembed";
err = _Py_InitializeFromConfig(&config);
if (_PyInitError_Failed(err)) {
_Py_ExitInitError(err);
}
dump_config();
Py_Finalize();
return 0;
}
static void set_all_env_vars_dev_mode(void) static void set_all_env_vars_dev_mode(void)
{ {
putenv("PYTHONMALLOC="); putenv("PYTHONMALLOC=");
@ -1257,7 +1280,8 @@ static struct TestCase TestCases[] = {
{"test_init_from_config", test_init_from_config}, {"test_init_from_config", test_init_from_config},
{"test_init_parse_argv", test_init_parse_argv}, {"test_init_parse_argv", test_init_parse_argv},
{"test_init_dont_parse_argv", test_init_dont_parse_argv}, {"test_init_dont_parse_argv", test_init_dont_parse_argv},
{"test_init_env", test_init_env}, {"test_init_compat_env", test_init_compat_env},
{"test_init_python_env", test_init_python_env},
{"test_init_env_dev_mode", test_init_env_dev_mode}, {"test_init_env_dev_mode", test_init_env_dev_mode},
{"test_init_env_dev_mode_alloc", test_init_env_dev_mode_alloc}, {"test_init_env_dev_mode_alloc", test_init_env_dev_mode_alloc},
{"test_init_dont_configure_locale", test_init_dont_configure_locale}, {"test_init_dont_configure_locale", test_init_dont_configure_locale},

View File

@ -107,9 +107,8 @@ static const char usage_6[] =
/* --- Global configuration variables ----------------------------- */ /* --- Global configuration variables ----------------------------- */
/* UTF-8 mode (PEP 540): if equals to 1, use the UTF-8 encoding, and change /* UTF-8 mode (PEP 540): if equals to 1, use the UTF-8 encoding, and change
stdin and stdout error handler to "surrogateescape". It is equal to stdin and stdout error handler to "surrogateescape". */
-1 by default: unknown, will be set by Py_Main() */ int Py_UTF8Mode = 0;
int Py_UTF8Mode = -1;
int Py_DebugFlag = 0; /* Needed by parser.c */ int Py_DebugFlag = 0; /* Needed by parser.c */
int Py_VerboseFlag = 0; /* Needed by import.c */ int Py_VerboseFlag = 0; /* Needed by import.c */
int Py_QuietFlag = 0; /* Needed by sysmodule.c */ int Py_QuietFlag = 0; /* Needed by sysmodule.c */

View File

@ -272,7 +272,16 @@ _PyPreConfig_InitCompatConfig(_PyPreConfig *config)
config->isolated = -1; config->isolated = -1;
config->use_environment = -1; config->use_environment = -1;
config->configure_locale = 1; config->configure_locale = 1;
config->utf8_mode = -1;
/* bpo-36443: C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
are disabled by default using the Compat configuration.
Py_UTF8Mode=1 enables the UTF-8 mode. PYTHONUTF8 environment variable
is ignored (even if use_environment=1). */
config->utf8_mode = 0;
config->coerce_c_locale = 0;
config->coerce_c_locale_warn = 0;
config->dev_mode = -1; config->dev_mode = -1;
config->allocator = PYMEM_ALLOCATOR_NOT_SET; config->allocator = PYMEM_ALLOCATOR_NOT_SET;
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
@ -353,6 +362,7 @@ _PyPreConfig_Copy(_PyPreConfig *config, const _PyPreConfig *config2)
{ {
#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR #define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
COPY_ATTR(_config_init);
COPY_ATTR(parse_argv); COPY_ATTR(parse_argv);
COPY_ATTR(isolated); COPY_ATTR(isolated);
COPY_ATTR(use_environment); COPY_ATTR(use_environment);
@ -393,6 +403,7 @@ _PyPreConfig_AsDict(const _PyPreConfig *config)
} \ } \
} while (0) } while (0)
SET_ITEM_INT(_config_init);
SET_ITEM_INT(parse_argv); SET_ITEM_INT(parse_argv);
SET_ITEM_INT(isolated); SET_ITEM_INT(isolated);
SET_ITEM_INT(use_environment); SET_ITEM_INT(use_environment);
@ -452,7 +463,9 @@ _PyPreConfig_GetGlobalConfig(_PyPreConfig *config)
COPY_FLAG(isolated, Py_IsolatedFlag); COPY_FLAG(isolated, Py_IsolatedFlag);
COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag); COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
COPY_FLAG(utf8_mode, Py_UTF8Mode); if (Py_UTF8Mode > 0) {
config->utf8_mode = Py_UTF8Mode;
}
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag); COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
#endif #endif