Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 425717f

Browse filesBrowse files
authored
bpo-36763: Fix encoding/locale tests in test_embed (GH-13443)
* Fix encoding and locale tests in test_embed.InitConfigTests. * InitConfigTests now only computes EXPECTED_CONFIG once. * Add tests for PYTHONWARNINGS and PYTHONPATH env vars
1 parent 9932fd9 commit 425717f
Copy full SHA for 425717f

File tree

Expand file treeCollapse file tree

2 files changed

+113
-96
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+113
-96
lines changed

‎Lib/test/test_embed.py

Copy file name to clipboardExpand all lines: Lib/test/test_embed.py
+103-87Lines changed: 103 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -296,13 +296,16 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
296296
})
297297
PYTHON_PRE_CONFIG = dict(DEFAULT_PRE_CONFIG,
298298
parse_argv=1,
299+
coerce_c_locale=GET_DEFAULT_CONFIG,
300+
utf8_mode=GET_DEFAULT_CONFIG,
299301
)
300302
ISOLATED_PRE_CONFIG = dict(DEFAULT_PRE_CONFIG,
301303
configure_locale=0,
302304
isolated=1,
303305
use_environment=0,
304306
utf8_mode=0,
305307
dev_mode=0,
308+
coerce_c_locale=0,
306309
)
307310

308311
COPY_PRE_CONFIG = [
@@ -435,6 +438,8 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
435438
('Py_LegacyWindowsStdioFlag', 'legacy_windows_stdio'),
436439
))
437440

441+
EXPECTED_CONFIG = None
442+
438443
def main_xoptions(self, xoptions_list):
439444
xoptions = {}
440445
for opt in xoptions_list:
@@ -445,37 +450,15 @@ def main_xoptions(self, xoptions_list):
445450
xoptions[opt] = True
446451
return xoptions
447452

448-
def get_expected_config(self, expected_preconfig, expected, env, api,
449-
add_path=None):
450-
if api == CONFIG_INIT_PYTHON:
451-
default_config = self.PYTHON_CORE_CONFIG
452-
elif api == CONFIG_INIT_ISOLATED:
453-
default_config = self.ISOLATED_CORE_CONFIG
454-
else:
455-
default_config = self.DEFAULT_CORE_CONFIG
456-
expected = dict(default_config, **expected)
457-
expected['_config_init'] = api
458-
453+
def _get_expected_config(self, env):
459454
code = textwrap.dedent('''
460455
import json
461456
import sys
462457
import _testinternalcapi
463458
464459
configs = _testinternalcapi.get_configs()
465-
core_config = configs['core_config']
466-
data = {
467-
'stdio_encoding': sys.stdout.encoding,
468-
'stdio_errors': sys.stdout.errors,
469-
'prefix': sys.prefix,
470-
'base_prefix': sys.base_prefix,
471-
'exec_prefix': sys.exec_prefix,
472-
'base_exec_prefix': sys.base_exec_prefix,
473-
'filesystem_encoding': sys.getfilesystemencoding(),
474-
'filesystem_errors': sys.getfilesystemencodeerrors(),
475-
'module_search_paths': core_config['module_search_paths'],
476-
}
477-
478-
data = json.dumps(data)
460+
461+
data = json.dumps(configs)
479462
data = data.encode('utf-8')
480463
sys.stdout.buffer.write(data)
481464
sys.stdout.buffer.flush()
@@ -484,10 +467,6 @@ def get_expected_config(self, expected_preconfig, expected, env, api,
484467
# Use -S to not import the site module: get the proper configuration
485468
# when test_embed is run from a venv (bpo-35313)
486469
args = [sys.executable, '-S', '-c', code]
487-
env = dict(env)
488-
if not expected['isolated']:
489-
env['PYTHONCOERCECLOCALE'] = '0'
490-
env['PYTHONUTF8'] = '0'
491470
proc = subprocess.run(args, env=env,
492471
stdout=subprocess.PIPE,
493472
stderr=subprocess.STDOUT)
@@ -496,10 +475,46 @@ def get_expected_config(self, expected_preconfig, expected, env, api,
496475
f"stdout={proc.stdout!r} stderr={proc.stderr!r}")
497476
stdout = proc.stdout.decode('utf-8')
498477
try:
499-
config = json.loads(stdout)
478+
return json.loads(stdout)
500479
except json.JSONDecodeError:
501480
self.fail(f"fail to decode stdout: {stdout!r}")
502481

482+
def get_expected_config(self, expected_preconfig, expected, env, api,
483+
add_path=None):
484+
cls = self.__class__
485+
if cls.EXPECTED_CONFIG is None:
486+
cls.EXPECTED_CONFIG = self._get_expected_config(env)
487+
configs = {key: dict(value)
488+
for key, value in self.EXPECTED_CONFIG.items()}
489+
490+
pre_config = configs['pre_config']
491+
for key, value in expected_preconfig.items():
492+
if value is self.GET_DEFAULT_CONFIG:
493+
expected_preconfig[key] = pre_config[key]
494+
495+
if not expected_preconfig['configure_locale'] or api == CONFIG_INIT:
496+
# there is no easy way to get the locale encoding before
497+
# setlocale(LC_CTYPE, "") is called: don't test encodings
498+
for key in ('filesystem_encoding', 'filesystem_errors',
499+
'stdio_encoding', 'stdio_errors'):
500+
expected[key] = self.IGNORE_CONFIG
501+
502+
if not expected_preconfig['configure_locale']:
503+
# UTF-8 Mode depends on the locale. There is no easy way
504+
# to guess if UTF-8 Mode will be enabled or not if the locale
505+
# is not configured.
506+
expected_preconfig['utf8_mode'] = self.IGNORE_CONFIG
507+
508+
if expected_preconfig['utf8_mode'] == 1:
509+
if expected['filesystem_encoding'] is self.GET_DEFAULT_CONFIG:
510+
expected['filesystem_encoding'] = 'utf-8'
511+
if expected['filesystem_errors'] is self.GET_DEFAULT_CONFIG:
512+
expected['filesystem_errors'] = self.UTF8_MODE_ERRORS
513+
if expected['stdio_encoding'] is self.GET_DEFAULT_CONFIG:
514+
expected['stdio_encoding'] = 'utf-8'
515+
if expected['stdio_errors'] is self.GET_DEFAULT_CONFIG:
516+
expected['stdio_errors'] = 'surrogateescape'
517+
503518
if expected['executable'] is self.GET_DEFAULT_CONFIG:
504519
if sys.platform == 'win32':
505520
expected['executable'] = self.test_exe
@@ -511,24 +526,28 @@ def get_expected_config(self, expected_preconfig, expected, env, api,
511526
if expected['program_name'] is self.GET_DEFAULT_CONFIG:
512527
expected['program_name'] = './_testembed'
513528

529+
core_config = configs['core_config']
514530
for key, value in expected.items():
515531
if value is self.GET_DEFAULT_CONFIG:
516-
expected[key] = config[key]
532+
expected[key] = core_config[key]
517533

534+
prepend_path = expected['module_search_path_env']
535+
if prepend_path is not None:
536+
expected['module_search_paths'] = [prepend_path, *expected['module_search_paths']]
518537
if add_path is not None:
519-
expected['module_search_paths'].append(add_path)
520-
521-
if not expected_preconfig['configure_locale']:
522-
# there is no easy way to get the locale encoding before
523-
# setlocale(LC_CTYPE, "") is called: don't test encodings
524-
for key in ('filesystem_encoding', 'filesystem_errors',
525-
'stdio_encoding', 'stdio_errors'):
526-
expected[key] = self.IGNORE_CONFIG
538+
expected['module_search_paths'] = [*expected['module_search_paths'], add_path]
527539

528-
return expected
540+
for key in self.COPY_PRE_CONFIG:
541+
if key not in expected_preconfig:
542+
expected_preconfig[key] = expected[key]
529543

530544
def check_pre_config(self, config, expected):
531-
self.assertEqual(config['pre_config'], expected)
545+
pre_config = dict(config['pre_config'])
546+
for key, value in list(expected.items()):
547+
if value is self.IGNORE_CONFIG:
548+
del pre_config[key]
549+
del expected[key]
550+
self.assertEqual(pre_config, expected)
532551

533552
def check_core_config(self, config, expected):
534553
core_config = dict(config['core_config'])
@@ -567,10 +586,6 @@ def check_config(self, testname, expected_config=None, expected_preconfig=None,
567586
for key in list(env):
568587
if key.startswith('PYTHON'):
569588
del env[key]
570-
# Disable C locale coercion and UTF-8 mode to not depend
571-
# on the current locale
572-
env['PYTHONCOERCECLOCALE'] = '0'
573-
env['PYTHONUTF8'] = '0'
574589

575590
if api == CONFIG_INIT_ISOLATED:
576591
default_preconfig = self.ISOLATED_PRE_CONFIG
@@ -583,12 +598,19 @@ def check_config(self, testname, expected_config=None, expected_preconfig=None,
583598
expected_preconfig = dict(default_preconfig, **expected_preconfig)
584599
if expected_config is None:
585600
expected_config = {}
586-
expected_config = self.get_expected_config(expected_preconfig,
587-
expected_config, env,
588-
api, add_path)
589-
for key in self.COPY_PRE_CONFIG:
590-
if key not in expected_preconfig:
591-
expected_preconfig[key] = expected_config[key]
601+
602+
if api == CONFIG_INIT_PYTHON:
603+
default_config = self.PYTHON_CORE_CONFIG
604+
elif api == CONFIG_INIT_ISOLATED:
605+
default_config = self.ISOLATED_CORE_CONFIG
606+
else:
607+
default_config = self.DEFAULT_CORE_CONFIG
608+
expected_config = dict(default_config, **expected_config)
609+
expected_config['_config_init'] = api
610+
611+
self.get_expected_config(expected_preconfig,
612+
expected_config, env,
613+
api, add_path)
592614

593615
out, err = self.run_embedded_interpreter(testname, env=env)
594616
if stderr is None and not expected_config['verbose']:
@@ -624,10 +646,6 @@ def test_init_global_config(self):
624646
'quiet': 1,
625647
'buffered_stdio': 0,
626648

627-
'stdio_encoding': 'utf-8',
628-
'stdio_errors': 'surrogateescape',
629-
'filesystem_encoding': 'utf-8',
630-
'filesystem_errors': self.UTF8_MODE_ERRORS,
631649
'user_site_directory': 0,
632650
'pathconfig_warnings': 0,
633651
}
@@ -650,8 +668,6 @@ def test_init_from_config(self):
650668

651669
'stdio_encoding': 'iso8859-1',
652670
'stdio_errors': 'replace',
653-
'filesystem_encoding': 'utf-8',
654-
'filesystem_errors': self.UTF8_MODE_ERRORS,
655671

656672
'pycache_prefix': 'conf_pycache_prefix',
657673
'program_name': './conf_program_name',
@@ -679,43 +695,42 @@ def test_init_from_config(self):
679695
}
680696
self.check_config("init_from_config", config, preconfig)
681697

682-
INIT_ENV_PRECONFIG = {
683-
'allocator': PYMEM_ALLOCATOR_MALLOC,
684-
}
685-
INIT_ENV_CONFIG = {
686-
'use_hash_seed': 1,
687-
'hash_seed': 42,
688-
'tracemalloc': 2,
689-
'import_time': 1,
690-
'malloc_stats': 1,
691-
'inspect': 1,
692-
'optimization_level': 2,
693-
'pycache_prefix': 'env_pycache_prefix',
694-
'write_bytecode': 0,
695-
'verbose': 1,
696-
'buffered_stdio': 0,
697-
'stdio_encoding': 'iso8859-1',
698-
'stdio_errors': 'replace',
699-
'user_site_directory': 0,
700-
'faulthandler': 1,
701-
}
702-
703698
def test_init_env(self):
704-
self.check_config("init_env", self.INIT_ENV_CONFIG, self.INIT_ENV_PRECONFIG)
699+
preconfig = {
700+
'allocator': PYMEM_ALLOCATOR_MALLOC,
701+
}
702+
config = {
703+
'use_hash_seed': 1,
704+
'hash_seed': 42,
705+
'tracemalloc': 2,
706+
'import_time': 1,
707+
'malloc_stats': 1,
708+
'inspect': 1,
709+
'optimization_level': 2,
710+
'module_search_path_env': '/my/path',
711+
'pycache_prefix': 'env_pycache_prefix',
712+
'write_bytecode': 0,
713+
'verbose': 1,
714+
'buffered_stdio': 0,
715+
'stdio_encoding': 'iso8859-1',
716+
'stdio_errors': 'replace',
717+
'user_site_directory': 0,
718+
'faulthandler': 1,
719+
'warnoptions': ['EnvVar'],
720+
}
721+
self.check_config("init_env", config, preconfig)
705722

706723
def test_init_env_dev_mode(self):
707-
preconfig = dict(self.INIT_ENV_PRECONFIG,
708-
allocator=PYMEM_ALLOCATOR_DEBUG)
709-
config = dict(self.INIT_ENV_CONFIG,
710-
dev_mode=1,
724+
preconfig = dict(allocator=PYMEM_ALLOCATOR_DEBUG)
725+
config = dict(dev_mode=1,
726+
faulthandler=1,
711727
warnoptions=['default'])
712728
self.check_config("init_env_dev_mode", config, preconfig)
713729

714730
def test_init_env_dev_mode_alloc(self):
715-
preconfig = dict(self.INIT_ENV_PRECONFIG,
716-
allocator=PYMEM_ALLOCATOR_MALLOC)
717-
config = dict(self.INIT_ENV_CONFIG,
718-
dev_mode=1,
731+
preconfig = dict(allocator=PYMEM_ALLOCATOR_MALLOC)
732+
config = dict(dev_mode=1,
733+
faulthandler=1,
719734
warnoptions=['default'])
720735
self.check_config("init_env_dev_mode_alloc", config, preconfig)
721736

@@ -800,6 +815,7 @@ def test_init_dont_configure_locale(self):
800815
# _PyPreConfig.configure_locale=0
801816
preconfig = {
802817
'configure_locale': 0,
818+
'coerce_c_locale': 0,
803819
}
804820
self.check_config("init_dont_configure_locale", {}, preconfig,
805821
api=CONFIG_INIT_PYTHON)

‎Programs/_testembed.c

Copy file name to clipboardExpand all lines: Programs/_testembed.c
+10-9Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ static int test_init_dont_parse_argv(void)
568568
}
569569

570570

571-
static void set_all_env_vars(void)
571+
static void set_most_env_vars(void)
572572
{
573573
putenv("PYTHONHASHSEED=42");
574574
putenv("PYTHONMALLOC=malloc");
@@ -585,13 +585,15 @@ static void set_all_env_vars(void)
585585
putenv("PYTHONNOUSERSITE=1");
586586
putenv("PYTHONFAULTHANDLER=1");
587587
putenv("PYTHONIOENCODING=iso8859-1:replace");
588-
/* FIXME: test PYTHONWARNINGS */
589-
/* FIXME: test PYTHONEXECUTABLE */
590-
/* FIXME: test PYTHONHOME */
591-
/* FIXME: test PYTHONDEBUG */
592-
/* FIXME: test PYTHONDUMPREFS */
593-
/* FIXME: test PYTHONCOERCECLOCALE */
594-
/* FIXME: test PYTHONPATH */
588+
}
589+
590+
591+
static void set_all_env_vars(void)
592+
{
593+
set_most_env_vars();
594+
595+
putenv("PYTHONWARNINGS=EnvVar");
596+
putenv("PYTHONPATH=/my/path");
595597
}
596598

597599

@@ -609,7 +611,6 @@ static int test_init_env(void)
609611

610612
static void set_all_env_vars_dev_mode(void)
611613
{
612-
set_all_env_vars();
613614
putenv("PYTHONMALLOC=");
614615
putenv("PYTHONFAULTHANDLER=");
615616
putenv("PYTHONDEVMODE=1");

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.