Skip to content

Commit 7926516

Browse files
authored
bpo-28180: Standard stream & FS encoding differ on Mac OS X (GH-2208)
In the C locale on Mac OS X, the default filesystem encoding used for operating system interfaces is UTF-8, but the default encoding used on the standard streams is still ASCII. Setting the POSIX locale also behaves differently from setting other locales on Mac OS X, so skip that in the test suite for now.
1 parent 23ec4b5 commit 7926516

File tree

1 file changed

+37
-21
lines changed

1 file changed

+37
-21
lines changed

Lib/test/test_c_locale_coercion.py

Lines changed: 37 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@
1616

1717
# Set our expectation for the default encoding used in the C locale
1818
# for the filesystem encoding and the standard streams
19+
C_LOCALE_STREAM_ENCODING = "ascii"
1920
if sys.platform == "darwin":
20-
EXPECTED_C_LOCALE_FSENCODING = "utf-8"
21+
C_LOCALE_FS_ENCODING = "utf-8"
2122
else:
22-
EXPECTED_C_LOCALE_FSENCODING = "ascii"
23+
C_LOCALE_FS_ENCODING = C_LOCALE_STREAM_ENCODING
2324

2425
# XXX (ncoghlan): The above is probably still wrong for:
2526
# * Windows when PYTHONLEGACYWINDOWSFSENCODING is set
@@ -52,15 +53,15 @@ class EncodingDetails(_EncodingDetails):
5253
])
5354

5455
@classmethod
55-
def get_expected_details(cls, expected_fsencoding):
56+
def get_expected_details(cls, fs_encoding, stream_encoding):
5657
"""Returns expected child process details for a given encoding"""
57-
_stream = expected_fsencoding + ":{}"
58+
_stream = stream_encoding + ":{}"
5859
# stdin and stdout should use surrogateescape either because the
5960
# coercion triggered, or because the C locale was detected
6061
stream_info = 2*[_stream.format("surrogateescape")]
6162
# stderr should always use backslashreplace
6263
stream_info.append(_stream.format("backslashreplace"))
63-
return dict(cls(expected_fsencoding, *stream_info)._asdict())
64+
return dict(cls(fs_encoding, *stream_info)._asdict())
6465

6566
@staticmethod
6667
def _handle_output_variations(data):
@@ -107,21 +108,22 @@ class _ChildProcessEncodingTestCase(unittest.TestCase):
107108

108109
def _check_child_encoding_details(self,
109110
env_vars,
110-
expected_fsencoding,
111+
expected_fs_encoding,
112+
expected_stream_encoding,
111113
expected_warning):
112114
"""Check the C locale handling for the given process environment
113115
114116
Parameters:
115-
expected_fsencoding: the encoding the child is expected to report
116-
allow_c_locale: setting to use for PYTHONALLOWCLOCALE
117-
None: don't set the variable at all
118-
str: the value set in the child's environment
117+
expected_fs_encoding: expected sys.getfilesystemencoding() result
118+
expected_stream_encoding: expected encoding for standard streams
119+
expected_warning: stderr output to expect (if any)
119120
"""
120121
result = EncodingDetails.get_child_details(env_vars)
121122
encoding_details, stderr_lines = result
122123
self.assertEqual(encoding_details,
123124
EncodingDetails.get_expected_details(
124-
expected_fsencoding))
125+
expected_fs_encoding,
126+
expected_stream_encoding))
125127
self.assertEqual(stderr_lines, expected_warning)
126128

127129
# Details of the shared library warning emitted at runtime
@@ -140,12 +142,17 @@ class LocaleWarningTests(_ChildProcessEncodingTestCase):
140142
def test_library_c_locale_warning(self):
141143
self.maxDiff = None
142144
for locale_to_set in ("C", "POSIX", "invalid.ascii"):
145+
# XXX (ncoghlan): Mac OS X doesn't behave as expected in the
146+
# POSIX locale, so we skip that for now
147+
if sys.platform == "darwin" and locale_to_set == "POSIX":
148+
continue
143149
var_dict = {
144150
"LC_ALL": locale_to_set
145151
}
146152
with self.subTest(forced_locale=locale_to_set):
147153
self._check_child_encoding_details(var_dict,
148-
EXPECTED_C_LOCALE_FSENCODING,
154+
C_LOCALE_FS_ENCODING,
155+
C_LOCALE_STREAM_ENCODING,
149156
[LIBRARY_C_LOCALE_WARNING])
150157

151158
# Details of the CLI locale coercion warning emitted at runtime
@@ -190,7 +197,8 @@ def test_external_target_locale_configuration(self):
190197
self.maxDiff = None
191198

192199
expected_warning = []
193-
expected_fsencoding = "utf-8"
200+
expected_fs_encoding = "utf-8"
201+
expected_stream_encoding = "utf-8"
194202

195203
base_var_dict = {
196204
"LANG": "",
@@ -209,7 +217,8 @@ def test_external_target_locale_configuration(self):
209217
var_dict = base_var_dict.copy()
210218
var_dict[env_var] = locale_to_set
211219
self._check_child_encoding_details(var_dict,
212-
expected_fsencoding,
220+
expected_fs_encoding,
221+
expected_stream_encoding,
213222
expected_warning)
214223

215224

@@ -220,12 +229,13 @@ def test_external_target_locale_configuration(self):
220229
class LocaleCoercionTests(_LocaleCoercionTargetsTestCase):
221230
# Test implicit reconfiguration of the environment during CLI startup
222231

223-
def _check_c_locale_coercion(self, expected_fsencoding, coerce_c_locale):
232+
def _check_c_locale_coercion(self, fs_encoding, stream_encoding, coerce_c_locale):
224233
"""Check the C locale handling for various configurations
225234
226235
Parameters:
227-
expected_fsencoding: the encoding the child is expected to report
228-
allow_c_locale: setting to use for PYTHONALLOWCLOCALE
236+
fs_encoding: expected sys.getfilesystemencoding() result
237+
stream_encoding: expected encoding for standard streams
238+
coerce_c_locale: setting to use for PYTHONCOERCECLOCALE
229239
None: don't set the variable at all
230240
str: the value set in the child's environment
231241
"""
@@ -246,6 +256,10 @@ def _check_c_locale_coercion(self, expected_fsencoding, coerce_c_locale):
246256
}
247257
for env_var in ("LANG", "LC_CTYPE"):
248258
for locale_to_set in ("", "C", "POSIX", "invalid.ascii"):
259+
# XXX (ncoghlan): Mac OS X doesn't behave as expected in the
260+
# POSIX locale, so we skip that for now
261+
if sys.platform == "darwin" and locale_to_set == "POSIX":
262+
continue
249263
with self.subTest(env_var=env_var,
250264
nominal_locale=locale_to_set,
251265
PYTHONCOERCECLOCALE=coerce_c_locale):
@@ -254,22 +268,24 @@ def _check_c_locale_coercion(self, expected_fsencoding, coerce_c_locale):
254268
if coerce_c_locale is not None:
255269
var_dict["PYTHONCOERCECLOCALE"] = coerce_c_locale
256270
self._check_child_encoding_details(var_dict,
257-
expected_fsencoding,
271+
fs_encoding,
272+
stream_encoding,
258273
expected_warning)
259274

260275
def test_test_PYTHONCOERCECLOCALE_not_set(self):
261276
# This should coerce to the first available target locale by default
262-
self._check_c_locale_coercion("utf-8", coerce_c_locale=None)
277+
self._check_c_locale_coercion("utf-8", "utf-8", coerce_c_locale=None)
263278

264279
def test_PYTHONCOERCECLOCALE_not_zero(self):
265280
# *Any* string other that "0" is considered "set" for our purposes
266281
# and hence should result in the locale coercion being enabled
267282
for setting in ("", "1", "true", "false"):
268-
self._check_c_locale_coercion("utf-8", coerce_c_locale=setting)
283+
self._check_c_locale_coercion("utf-8", "utf-8", coerce_c_locale=setting)
269284

270285
def test_PYTHONCOERCECLOCALE_set_to_zero(self):
271286
# The setting "0" should result in the locale coercion being disabled
272-
self._check_c_locale_coercion(EXPECTED_C_LOCALE_FSENCODING,
287+
self._check_c_locale_coercion(C_LOCALE_FS_ENCODING,
288+
C_LOCALE_STREAM_ENCODING,
273289
coerce_c_locale="0")
274290

275291

0 commit comments

Comments
 (0)