@@ -85,12 +85,16 @@ class EncodingDetails(_EncodingDetails):
85
85
])
86
86
87
87
@classmethod
88
- def get_expected_details (cls , coercion_expected , fs_encoding , stream_encoding , env_vars ):
88
+ def get_expected_details (cls , coercion_expected , fs_encoding , stream_encoding , stream_errors , env_vars ):
89
89
"""Returns expected child process details for a given encoding"""
90
90
_stream = stream_encoding + ":{}"
91
- # stdin and stdout should use surrogateescape either because the
92
- # coercion triggered, or because the C locale was detected
93
- stream_info = 2 * [_stream .format ("surrogateescape" )]
91
+ if stream_errors is None :
92
+ # stdin and stdout should use surrogateescape either because the
93
+ # coercion triggered, or because the C locale was detected
94
+ stream_errors = "surrogateescape"
95
+
96
+ stream_info = [_stream .format (stream_errors )] * 2
97
+
94
98
# stderr should always use backslashreplace
95
99
stream_info .append (_stream .format ("backslashreplace" ))
96
100
expected_lang = env_vars .get ("LANG" , "not set" ).lower ()
@@ -189,6 +193,7 @@ def _check_child_encoding_details(self,
189
193
env_vars ,
190
194
expected_fs_encoding ,
191
195
expected_stream_encoding ,
196
+ expected_stream_errors ,
192
197
expected_warnings ,
193
198
coercion_expected ):
194
199
"""Check the C locale handling for the given process environment
@@ -204,6 +209,7 @@ def _check_child_encoding_details(self,
204
209
coercion_expected ,
205
210
expected_fs_encoding ,
206
211
expected_stream_encoding ,
212
+ expected_stream_errors ,
207
213
env_vars
208
214
)
209
215
self .assertEqual (encoding_details , expected_details )
@@ -234,6 +240,8 @@ def test_external_target_locale_configuration(self):
234
240
"LANG" : "" ,
235
241
"LC_CTYPE" : "" ,
236
242
"LC_ALL" : "" ,
243
+ "PYTHONCOERCECLOCALE" : "" ,
244
+ "PYTHONIOENCODING" : "" ,
237
245
}
238
246
for env_var in ("LANG" , "LC_CTYPE" ):
239
247
for locale_to_set in AVAILABLE_TARGETS :
@@ -250,10 +258,43 @@ def test_external_target_locale_configuration(self):
250
258
self ._check_child_encoding_details (var_dict ,
251
259
expected_fs_encoding ,
252
260
expected_stream_encoding ,
261
+ expected_stream_errors = None ,
253
262
expected_warnings = None ,
254
263
coercion_expected = False )
255
264
265
+ def test_with_ioencoding (self ):
266
+ # Explicitly setting a target locale should give the same behaviour as
267
+ # is seen when implicitly coercing to that target locale
268
+ self .maxDiff = None
256
269
270
+ expected_fs_encoding = "utf-8"
271
+ expected_stream_encoding = "utf-8"
272
+
273
+ base_var_dict = {
274
+ "LANG" : "" ,
275
+ "LC_CTYPE" : "" ,
276
+ "LC_ALL" : "" ,
277
+ "PYTHONCOERCECLOCALE" : "" ,
278
+ "PYTHONIOENCODING" : "UTF-8" ,
279
+ }
280
+ for env_var in ("LANG" , "LC_CTYPE" ):
281
+ for locale_to_set in AVAILABLE_TARGETS :
282
+ # XXX (ncoghlan): LANG=UTF-8 doesn't appear to work as
283
+ # expected, so skip that combination for now
284
+ # See https://bugs.python.org/issue30672 for discussion
285
+ if env_var == "LANG" and locale_to_set == "UTF-8" :
286
+ continue
287
+
288
+ with self .subTest (env_var = env_var ,
289
+ configured_locale = locale_to_set ):
290
+ var_dict = base_var_dict .copy ()
291
+ var_dict [env_var ] = locale_to_set
292
+ self ._check_child_encoding_details (var_dict ,
293
+ expected_fs_encoding ,
294
+ expected_stream_encoding ,
295
+ expected_stream_errors = "strict" ,
296
+ expected_warnings = None ,
297
+ coercion_expected = False )
257
298
258
299
@test .support .cpython_only
259
300
@unittest .skipUnless (sysconfig .get_config_var ("PY_COERCE_C_LOCALE" ),
@@ -292,18 +333,43 @@ def _check_c_locale_coercion(self,
292
333
"LANG" : "" ,
293
334
"LC_CTYPE" : "" ,
294
335
"LC_ALL" : "" ,
336
+ "PYTHONCOERCECLOCALE" : "" ,
337
+ "PYTHONIOENCODING" : "" ,
295
338
}
296
339
base_var_dict .update (extra_vars )
297
- for env_var in ("LANG" , "LC_CTYPE" ):
298
- for locale_to_set in ("" , "C" , "POSIX" , "invalid.ascii" ):
299
- # XXX (ncoghlan): *BSD platforms don't behave as expected in the
300
- # POSIX locale, so we skip that for now
301
- # See https://bugs.python.org/issue30672 for discussion
302
- if locale_to_set == "POSIX" :
303
- continue
340
+ if coerce_c_locale is not None :
341
+ base_var_dict ["PYTHONCOERCECLOCALE" ] = coerce_c_locale
342
+
343
+ # Check behaviour for the default locale
344
+ with self .subTest (default_locale = True ,
345
+ PYTHONCOERCECLOCALE = coerce_c_locale ):
346
+ if EXPECT_COERCION_IN_DEFAULT_LOCALE :
347
+ _expected_warnings = expected_warnings
348
+ _coercion_expected = coercion_expected
349
+ else :
350
+ _expected_warnings = None
351
+ _coercion_expected = False
352
+ # On Android CLI_COERCION_WARNING is not printed when all the
353
+ # locale environment variables are undefined or empty. When
354
+ # this code path is run with environ['LC_ALL'] == 'C', then
355
+ # LEGACY_LOCALE_WARNING is printed.
356
+ if (support .is_android and
357
+ _expected_warnings == [CLI_COERCION_WARNING ]):
358
+ _expected_warnings = None
359
+ self ._check_child_encoding_details (base_var_dict ,
360
+ fs_encoding ,
361
+ stream_encoding ,
362
+ None ,
363
+ _expected_warnings ,
364
+ _coercion_expected )
365
+
366
+ # Check behaviour for explicitly configured locales
367
+ for locale_to_set in EXPECTED_C_LOCALE_EQUIVALENTS :
368
+ for env_var in ("LANG" , "LC_CTYPE" ):
304
369
with self .subTest (env_var = env_var ,
305
370
nominal_locale = locale_to_set ,
306
- PYTHONCOERCECLOCALE = coerce_c_locale ):
371
+ PYTHONCOERCECLOCALE = coerce_c_locale ,
372
+ PYTHONIOENCODING = "" ):
307
373
var_dict = base_var_dict .copy ()
308
374
var_dict [env_var ] = locale_to_set
309
375
if coerce_c_locale is not None :
@@ -312,6 +378,7 @@ def _check_c_locale_coercion(self,
312
378
self ._check_child_encoding_details (var_dict ,
313
379
fs_encoding ,
314
380
stream_encoding ,
381
+ None ,
315
382
expected_warnings ,
316
383
coercion_expected )
317
384
0 commit comments