@@ -112,12 +112,16 @@ class EncodingDetails(_EncodingDetails):
112112 ])
113113
114114 @classmethod
115- def get_expected_details (cls , coercion_expected , fs_encoding , stream_encoding , env_vars ):
115+ def get_expected_details (cls , coercion_expected , fs_encoding , stream_encoding , stream_errors , env_vars ):
116116 """Returns expected child process details for a given encoding"""
117117 _stream = stream_encoding + ":{}"
118- # stdin and stdout should use surrogateescape either because the
119- # coercion triggered, or because the C locale was detected
120- stream_info = 2 * [_stream .format ("surrogateescape" )]
118+ if stream_errors is None :
119+ # stdin and stdout should use surrogateescape either because the
120+ # coercion triggered, or because the C locale was detected
121+ stream_errors = "surrogateescape"
122+
123+ stream_info = [_stream .format (stream_errors )] * 2
124+
121125 # stderr should always use backslashreplace
122126 stream_info .append (_stream .format ("backslashreplace" ))
123127 expected_lang = env_vars .get ("LANG" , "not set" )
@@ -210,6 +214,7 @@ def _check_child_encoding_details(self,
210214 env_vars ,
211215 expected_fs_encoding ,
212216 expected_stream_encoding ,
217+ expected_stream_errors ,
213218 expected_warnings ,
214219 coercion_expected ):
215220 """Check the C locale handling for the given process environment
@@ -225,6 +230,7 @@ def _check_child_encoding_details(self,
225230 coercion_expected ,
226231 expected_fs_encoding ,
227232 expected_stream_encoding ,
233+ expected_stream_errors ,
228234 env_vars
229235 )
230236 self .assertEqual (encoding_details , expected_details )
@@ -257,6 +263,7 @@ def test_external_target_locale_configuration(self):
257263 "LC_CTYPE" : "" ,
258264 "LC_ALL" : "" ,
259265 "PYTHONCOERCECLOCALE" : "" ,
266+ "PYTHONIOENCODING" : "" ,
260267 }
261268 for env_var in ("LANG" , "LC_CTYPE" ):
262269 for locale_to_set in AVAILABLE_TARGETS :
@@ -273,10 +280,43 @@ def test_external_target_locale_configuration(self):
273280 self ._check_child_encoding_details (var_dict ,
274281 expected_fs_encoding ,
275282 expected_stream_encoding ,
283+ expected_stream_errors = None ,
276284 expected_warnings = None ,
277285 coercion_expected = False )
278286
287+ def test_with_ioencoding (self ):
288+ # Explicitly setting a target locale should give the same behaviour as
289+ # is seen when implicitly coercing to that target locale
290+ self .maxDiff = None
291+
292+ expected_fs_encoding = "utf-8"
293+ expected_stream_encoding = "utf-8"
279294
295+ base_var_dict = {
296+ "LANG" : "" ,
297+ "LC_CTYPE" : "" ,
298+ "LC_ALL" : "" ,
299+ "PYTHONCOERCECLOCALE" : "" ,
300+ "PYTHONIOENCODING" : "UTF-8" ,
301+ }
302+ for env_var in ("LANG" , "LC_CTYPE" ):
303+ for locale_to_set in AVAILABLE_TARGETS :
304+ # XXX (ncoghlan): LANG=UTF-8 doesn't appear to work as
305+ # expected, so skip that combination for now
306+ # See https://bugs.python.org/issue30672 for discussion
307+ if env_var == "LANG" and locale_to_set == "UTF-8" :
308+ continue
309+
310+ with self .subTest (env_var = env_var ,
311+ configured_locale = locale_to_set ):
312+ var_dict = base_var_dict .copy ()
313+ var_dict [env_var ] = locale_to_set
314+ self ._check_child_encoding_details (var_dict ,
315+ expected_fs_encoding ,
316+ expected_stream_encoding ,
317+ expected_stream_errors = "strict" ,
318+ expected_warnings = None ,
319+ coercion_expected = False )
280320
281321@support .cpython_only
282322@unittest .skipUnless (sysconfig .get_config_var ("PY_COERCE_C_LOCALE" ),
@@ -316,6 +356,7 @@ def _check_c_locale_coercion(self,
316356 "LC_CTYPE" : "" ,
317357 "LC_ALL" : "" ,
318358 "PYTHONCOERCECLOCALE" : "" ,
359+ "PYTHONIOENCODING" : "" ,
319360 }
320361 base_var_dict .update (extra_vars )
321362 if coerce_c_locale is not None :
@@ -340,6 +381,7 @@ def _check_c_locale_coercion(self,
340381 self ._check_child_encoding_details (base_var_dict ,
341382 fs_encoding ,
342383 stream_encoding ,
384+ None ,
343385 _expected_warnings ,
344386 _coercion_expected )
345387
@@ -348,13 +390,15 @@ def _check_c_locale_coercion(self,
348390 for env_var in ("LANG" , "LC_CTYPE" ):
349391 with self .subTest (env_var = env_var ,
350392 nominal_locale = locale_to_set ,
351- PYTHONCOERCECLOCALE = coerce_c_locale ):
393+ PYTHONCOERCECLOCALE = coerce_c_locale ,
394+ PYTHONIOENCODING = "" ):
352395 var_dict = base_var_dict .copy ()
353396 var_dict [env_var ] = locale_to_set
354397 # Check behaviour on successful coercion
355398 self ._check_child_encoding_details (var_dict ,
356399 fs_encoding ,
357400 stream_encoding ,
401+ None ,
358402 expected_warnings ,
359403 coercion_expected )
360404
0 commit comments