Skip to content

Commit 20e1e25

Browse files
authored
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.
1 parent bc2aa81 commit 20e1e25

File tree

4 files changed

+77
-11
lines changed

4 files changed

+77
-11
lines changed

Lib/test/test_embed.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
287287
IGNORE_CONFIG = object()
288288

289289
PRE_CONFIG_COMPAT = {
290+
'_config_init': API_COMPAT,
290291
'allocator': PYMEM_ALLOCATOR_NOT_SET,
291292
'parse_argv': 0,
292293
'configure_locale': 1,
@@ -299,11 +300,13 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
299300
'legacy_windows_fs_encoding': 0,
300301
})
301302
PRE_CONFIG_PYTHON = dict(PRE_CONFIG_COMPAT,
303+
_config_init=API_PYTHON,
302304
parse_argv=1,
303305
coerce_c_locale=GET_DEFAULT_CONFIG,
304306
utf8_mode=GET_DEFAULT_CONFIG,
305307
)
306308
PRE_CONFIG_ISOLATED = dict(PRE_CONFIG_COMPAT,
309+
_config_init=API_ISOLATED,
307310
configure_locale=0,
308311
isolated=1,
309312
use_environment=0,
@@ -388,10 +391,12 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
388391
})
389392

390393
CORE_CONFIG_PYTHON = dict(CORE_CONFIG_COMPAT,
394+
_config_init=API_PYTHON,
391395
configure_c_stdio=1,
392396
parse_argv=1,
393397
)
394398
CORE_CONFIG_ISOLATED = dict(CORE_CONFIG_COMPAT,
399+
_config_init=API_ISOLATED,
395400
isolated=1,
396401
use_environment=0,
397402
user_site_directory=0,
@@ -611,7 +616,6 @@ def check_config(self, testname, expected_config=None,
611616
else:
612617
default_config = self.CORE_CONFIG_COMPAT
613618
expected_config = dict(default_config, **expected_config)
614-
expected_config['_config_init'] = api
615619

616620
self.get_expected_config(expected_preconfig,
617621
expected_config, env,
@@ -708,10 +712,9 @@ def test_init_from_config(self):
708712
self.check_config("test_init_from_config", config, preconfig,
709713
api=API_COMPAT)
710714

711-
def test_init_env(self):
715+
def test_init_compat_env(self):
712716
preconfig = {
713717
'allocator': PYMEM_ALLOCATOR_MALLOC,
714-
'utf8_mode': 1,
715718
}
716719
config = {
717720
'use_hash_seed': 1,
@@ -732,9 +735,36 @@ def test_init_env(self):
732735
'faulthandler': 1,
733736
'warnoptions': ['EnvVar'],
734737
}
735-
self.check_config("test_init_env", config, preconfig,
738+
self.check_config("test_init_compat_env", config, preconfig,
736739
api=API_COMPAT)
737740

741+
def test_init_python_env(self):
742+
preconfig = {
743+
'allocator': PYMEM_ALLOCATOR_MALLOC,
744+
'utf8_mode': 1,
745+
}
746+
config = {
747+
'use_hash_seed': 1,
748+
'hash_seed': 42,
749+
'tracemalloc': 2,
750+
'import_time': 1,
751+
'malloc_stats': 1,
752+
'inspect': 1,
753+
'optimization_level': 2,
754+
'module_search_path_env': '/my/path',
755+
'pycache_prefix': 'env_pycache_prefix',
756+
'write_bytecode': 0,
757+
'verbose': 1,
758+
'buffered_stdio': 0,
759+
'stdio_encoding': 'iso8859-1',
760+
'stdio_errors': 'replace',
761+
'user_site_directory': 0,
762+
'faulthandler': 1,
763+
'warnoptions': ['EnvVar'],
764+
}
765+
self.check_config("test_init_python_env", config, preconfig,
766+
api=API_PYTHON)
767+
738768
def test_init_env_dev_mode(self):
739769
preconfig = dict(allocator=PYMEM_ALLOCATOR_DEBUG)
740770
config = dict(dev_mode=1,

Programs/_testembed.c

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ static void set_all_env_vars(void)
638638
}
639639

640640

641-
static int test_init_env(void)
641+
static int test_init_compat_env(void)
642642
{
643643
/* Test initialization from environment variables */
644644
Py_IgnoreEnvironmentFlag = 0;
@@ -650,6 +650,29 @@ static int test_init_env(void)
650650
}
651651

652652

653+
static int test_init_python_env(void)
654+
{
655+
_PyInitError err;
656+
657+
set_all_env_vars();
658+
659+
_PyCoreConfig config;
660+
err = _PyCoreConfig_InitPythonConfig(&config);
661+
if (_PyInitError_Failed(err)) {
662+
_Py_ExitInitError(err);
663+
}
664+
config.program_name = L"./_testembed";
665+
666+
err = _Py_InitializeFromConfig(&config);
667+
if (_PyInitError_Failed(err)) {
668+
_Py_ExitInitError(err);
669+
}
670+
dump_config();
671+
Py_Finalize();
672+
return 0;
673+
}
674+
675+
653676
static void set_all_env_vars_dev_mode(void)
654677
{
655678
putenv("PYTHONMALLOC=");
@@ -1257,7 +1280,8 @@ static struct TestCase TestCases[] = {
12571280
{"test_init_from_config", test_init_from_config},
12581281
{"test_init_parse_argv", test_init_parse_argv},
12591282
{"test_init_dont_parse_argv", test_init_dont_parse_argv},
1260-
{"test_init_env", test_init_env},
1283+
{"test_init_compat_env", test_init_compat_env},
1284+
{"test_init_python_env", test_init_python_env},
12611285
{"test_init_env_dev_mode", test_init_env_dev_mode},
12621286
{"test_init_env_dev_mode_alloc", test_init_env_dev_mode_alloc},
12631287
{"test_init_dont_configure_locale", test_init_dont_configure_locale},

Python/coreconfig.c

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,8 @@ static const char usage_6[] =
107107
/* --- Global configuration variables ----------------------------- */
108108

109109
/* UTF-8 mode (PEP 540): if equals to 1, use the UTF-8 encoding, and change
110-
stdin and stdout error handler to "surrogateescape". It is equal to
111-
-1 by default: unknown, will be set by Py_Main() */
112-
int Py_UTF8Mode = -1;
110+
stdin and stdout error handler to "surrogateescape". */
111+
int Py_UTF8Mode = 0;
113112
int Py_DebugFlag = 0; /* Needed by parser.c */
114113
int Py_VerboseFlag = 0; /* Needed by import.c */
115114
int Py_QuietFlag = 0; /* Needed by sysmodule.c */

Python/preconfig.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,16 @@ _PyPreConfig_InitCompatConfig(_PyPreConfig *config)
272272
config->isolated = -1;
273273
config->use_environment = -1;
274274
config->configure_locale = 1;
275-
config->utf8_mode = -1;
275+
276+
/* bpo-36443: C locale coercion (PEP 538) and UTF-8 Mode (PEP 540)
277+
are disabled by default using the Compat configuration.
278+
279+
Py_UTF8Mode=1 enables the UTF-8 mode. PYTHONUTF8 environment variable
280+
is ignored (even if use_environment=1). */
281+
config->utf8_mode = 0;
282+
config->coerce_c_locale = 0;
283+
config->coerce_c_locale_warn = 0;
284+
276285
config->dev_mode = -1;
277286
config->allocator = PYMEM_ALLOCATOR_NOT_SET;
278287
#ifdef MS_WINDOWS
@@ -353,6 +362,7 @@ _PyPreConfig_Copy(_PyPreConfig *config, const _PyPreConfig *config2)
353362
{
354363
#define COPY_ATTR(ATTR) config->ATTR = config2->ATTR
355364

365+
COPY_ATTR(_config_init);
356366
COPY_ATTR(parse_argv);
357367
COPY_ATTR(isolated);
358368
COPY_ATTR(use_environment);
@@ -393,6 +403,7 @@ _PyPreConfig_AsDict(const _PyPreConfig *config)
393403
} \
394404
} while (0)
395405

406+
SET_ITEM_INT(_config_init);
396407
SET_ITEM_INT(parse_argv);
397408
SET_ITEM_INT(isolated);
398409
SET_ITEM_INT(use_environment);
@@ -452,7 +463,9 @@ _PyPreConfig_GetGlobalConfig(_PyPreConfig *config)
452463

453464
COPY_FLAG(isolated, Py_IsolatedFlag);
454465
COPY_NOT_FLAG(use_environment, Py_IgnoreEnvironmentFlag);
455-
COPY_FLAG(utf8_mode, Py_UTF8Mode);
466+
if (Py_UTF8Mode > 0) {
467+
config->utf8_mode = Py_UTF8Mode;
468+
}
456469
#ifdef MS_WINDOWS
457470
COPY_FLAG(legacy_windows_fs_encoding, Py_LegacyWindowsFSEncodingFlag);
458471
#endif

0 commit comments

Comments
 (0)