Skip to content

Commit 4e6d502

Browse files
committed
Check standard stream settings in locale coercion tests
1 parent ec4f2ea commit 4e6d502

File tree

1 file changed

+80
-41
lines changed

1 file changed

+80
-41
lines changed

Lib/test/test_c_locale_coercion.py

Lines changed: 80 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66
import sysconfig
77
import shutil
88
import subprocess
9+
from collections import namedtuple
10+
911
import test.support
1012
from test.support.script_helper import (
1113
run_python_until_end,
@@ -37,76 +39,113 @@ def _set_locale_in_subprocess(locale_name, category):
3739
"or PYTHONCOERCECLOCALE=0 to disable this locale coercion behaviour)."
3840
)
3941

40-
@test.support.cpython_only
41-
@unittest.skipUnless(sysconfig.get_config_var("PY_COERCE_C_LOCALE"),
42-
"C locale coercion disabled at build time")
43-
class LocaleOverrideTests(unittest.TestCase):
42+
_EncodingDetails = namedtuple("EncodingDetails",
43+
"fsencoding stdin_info stdout_info stderr_info")
44+
45+
class EncodingDetails(_EncodingDetails):
46+
CHILD_PROCESS_SCRIPT = ";".join([
47+
"import sys",
48+
"print(sys.getfilesystemencoding())",
49+
"print(sys.stdin.encoding + ':' + sys.stdin.errors)",
50+
"print(sys.stdout.encoding + ':' + sys.stdout.errors)",
51+
"print(sys.stderr.encoding + ':' + sys.stderr.errors)",
52+
])
4453

4554
@classmethod
46-
def setUpClass(cls):
47-
for target_locale, target_category, env_updates in _C_UTF8_LOCALES:
48-
if _set_locale_in_subprocess(target_locale, target_category):
49-
break
50-
else:
51-
raise unittest.SkipTest("No C-with-UTF-8 locale available")
52-
cls.EXPECTED_COERCION_WARNING = CLI_COERCION_WARNING_FMT.format(
53-
env_updates, target_locale
54-
)
55+
def get_expected_details(cls, expected_fsencoding):
56+
"""Returns expected child process details for a given encoding"""
57+
_stream = expected_fsencoding + ":{}"
58+
# stdin and stdout should use surrogateescape either because the
59+
# coercion triggered, or because the C locale was detected
60+
stream_info = 2*[_stream.format("surrogateescape")]
61+
# stderr should always use backslashreplace
62+
stream_info.append(_stream.format("backslashreplace"))
63+
return dict(cls(expected_fsencoding, *stream_info)._asdict())
64+
65+
@staticmethod
66+
def _replace_ascii_alias(data):
67+
"""ASCII may be reported as ANSI_X3.4-1968, so replace it in output"""
68+
return data.replace(b"ANSI_X3.4-1968", b"ascii")
5569

56-
def _get_child_fsencoding(self, env_vars):
57-
"""Retrieves sys.getfilesystemencoding() from a child process
70+
@classmethod
71+
def get_child_details(cls, env_vars):
72+
"""Retrieves fsencoding and standard stream details from a child process
5873
59-
Returns (fsencoding, stderr_lines):
74+
Returns (encoding_details, stderr_lines):
6075
61-
- fsencoding: a lowercase str value with the child's fsencoding
76+
- encoding_details: EncodingDetails for eager decoding
6277
- stderr_lines: result of calling splitlines() on the stderr output
6378
6479
The child is run in isolated mode if the current interpreter supports
6580
that.
6681
"""
67-
cmd = "import sys; print(sys.getfilesystemencoding().lower())"
6882
result, py_cmd = run_python_until_end(
69-
"-c", cmd,
83+
"-c", cls.CHILD_PROCESS_SCRIPT,
7084
__isolated=True,
7185
**env_vars
7286
)
7387
if not result.rc == 0:
7488
result.fail(py_cmd)
7589
# All subprocess outputs in this test case should be pure ASCII
76-
child_fsencoding = result.out.decode("ascii").rstrip()
77-
child_stderr_lines = result.err.decode("ascii").rstrip().splitlines()
78-
return child_fsencoding, child_stderr_lines
90+
adjusted_output = cls._replace_ascii_alias(result.out)
91+
stdout_lines = adjusted_output.decode("ascii").rstrip().splitlines()
92+
child_encoding_details = dict(cls(*stdout_lines)._asdict())
93+
stderr_lines = result.err.decode("ascii").rstrip().splitlines()
94+
return child_encoding_details, stderr_lines
7995

8096

81-
def test_C_utf8_locale(self):
82-
# Ensure the C.UTF-8 locale is accepted entirely without complaint
83-
base_var_dict = {
84-
"LANG": "",
85-
"LC_CTYPE": "",
86-
"LC_ALL": "",
87-
}
88-
for env_var in base_var_dict:
89-
with self.subTest(env_var=env_var):
90-
var_dict = base_var_dict.copy()
91-
var_dict[env_var] = "C.UTF-8"
92-
fsencoding, stderr_lines = self._get_child_fsencoding(var_dict)
93-
self.assertEqual(fsencoding, "utf-8")
94-
self.assertFalse(stderr_lines)
97+
@test.support.cpython_only
98+
@unittest.skipUnless(sysconfig.get_config_var("PY_COERCE_C_LOCALE"),
99+
"C locale coercion disabled at build time")
100+
class LocaleOverrideTests(unittest.TestCase):
101+
102+
@classmethod
103+
def setUpClass(cls):
104+
for target_locale, target_category, env_updates in _C_UTF8_LOCALES:
105+
if _set_locale_in_subprocess(target_locale, target_category):
106+
break
107+
else:
108+
raise unittest.SkipTest("No C-with-UTF-8 locale available")
109+
cls.EXPECTED_COERCION_WARNING = CLI_COERCION_WARNING_FMT.format(
110+
env_updates, target_locale
111+
)
112+
113+
def _check_child_encoding_details(self,
114+
env_vars,
115+
expected_fsencoding,
116+
expected_warning):
117+
"""Check the C locale handling for various configurations
118+
119+
Parameters:
120+
expected_fsencoding: the encoding the child is expected to report
121+
allow_c_locale: setting to use for PYTHONALLOWCLOCALE
122+
None: don't set the variable at all
123+
str: the value set in the child's environment
124+
"""
125+
result = EncodingDetails.get_child_details(env_vars)
126+
encoding_details, stderr_lines = result
127+
self.assertEqual(encoding_details,
128+
EncodingDetails.get_expected_details(
129+
expected_fsencoding))
130+
self.assertEqual(stderr_lines, expected_warning)
95131

96132

97133
def _check_c_locale_coercion(self, expected_fsencoding, coerce_c_locale):
98-
"""Check the handling of the C locale for various configurations
134+
"""Check the C locale handling for various configurations
99135
100136
Parameters:
101137
expected_fsencoding: the encoding the child is expected to report
102138
allow_c_locale: setting to use for PYTHONALLOWCLOCALE
103139
None: don't set the variable at all
104140
str: the value set in the child's environment
105141
"""
142+
143+
# Check for expected warning on stderr if C locale is coerced
106144
expected_warning = []
107145
if coerce_c_locale != "0":
108-
# Check C locale is coerced with a warning on stderr
109146
expected_warning.append(self.EXPECTED_COERCION_WARNING)
147+
148+
self.maxDiff = None
110149
base_var_dict = {
111150
"LANG": "",
112151
"LC_CTYPE": "",
@@ -121,9 +160,9 @@ def _check_c_locale_coercion(self, expected_fsencoding, coerce_c_locale):
121160
var_dict[env_var] = locale_to_set
122161
if coerce_c_locale is not None:
123162
var_dict["PYTHONCOERCECLOCALE"] = coerce_c_locale
124-
fsencoding, stderr_lines = self._get_child_fsencoding(var_dict)
125-
self.assertEqual(fsencoding, expected_fsencoding)
126-
self.assertEqual(stderr_lines, expected_warning)
163+
self._check_child_encoding_details(var_dict,
164+
expected_fsencoding,
165+
expected_warning)
127166

128167

129168
def test_test_PYTHONCOERCECLOCALE_not_set(self):

0 commit comments

Comments
 (0)