Skip to content

Commit e10dc94

Browse files
authored
Fix stdio_encoding, stdio_errors (#6617)
* stdio_errors, stdio_encodings * add ascii * unmark tests
1 parent e58cf84 commit e10dc94

File tree

6 files changed

+61
-21
lines changed

6 files changed

+61
-21
lines changed

.cspell.dict/python-more.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ PYTHONHASHSEED
178178
PYTHONHOME
179179
PYTHONINSPECT
180180
PYTHONINTMAXSTRDIGITS
181+
PYTHONIOENCODING
181182
PYTHONNODEBUGRANGES
182183
PYTHONNOUSERSITE
183184
PYTHONOPTIMIZE

Lib/test/test_tarfile.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2494,8 +2494,6 @@ def test_test_command_invalid_file(self):
24942494
finally:
24952495
os_helper.unlink(tmpname)
24962496

2497-
# TODO: RUSTPYTHON
2498-
@unittest.expectedFailure
24992497
def test_list_command(self):
25002498
for tar_name in testtarnames:
25012499
with support.captured_stdout() as t:
@@ -2507,8 +2505,6 @@ def test_list_command(self):
25072505
PYTHONIOENCODING='ascii')
25082506
self.assertEqual(out, expected)
25092507

2510-
# TODO: RUSTPYTHON
2511-
@unittest.expectedFailure
25122508
def test_list_command_verbose(self):
25132509
for tar_name in testtarnames:
25142510
with support.captured_stdout() as t:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../../Lib/encodings/ascii.py

crates/vm/src/vm/mod.rs

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -261,17 +261,28 @@ impl VirtualMachine {
261261
Ok(())
262262
}
263263

264-
fn import_utf8_encodings(&mut self) -> PyResult<()> {
264+
fn import_ascii_utf8_encodings(&mut self) -> PyResult<()> {
265265
import::import_frozen(self, "codecs")?;
266-
// FIXME: See corresponding part of `core_frozen_inits`
267-
// let encoding_module_name = if cfg!(feature = "freeze-stdlib") {
268-
// "encodings.utf_8"
269-
// } else {
270-
// "encodings_utf_8"
271-
// };
272-
let encoding_module_name = "encodings_utf_8";
273-
let encoding_module = import::import_frozen(self, encoding_module_name)?;
274-
let getregentry = encoding_module.get_attr("getregentry", self)?;
266+
267+
// Use dotted names when freeze-stdlib is enabled (modules come from Lib/encodings/),
268+
// otherwise use underscored names (modules come from core_modules/).
269+
let (ascii_module_name, utf8_module_name) = if cfg!(feature = "freeze-stdlib") {
270+
("encodings.ascii", "encodings.utf_8")
271+
} else {
272+
("encodings_ascii", "encodings_utf_8")
273+
};
274+
275+
// Register ascii encoding
276+
let ascii_module = import::import_frozen(self, ascii_module_name)?;
277+
let getregentry = ascii_module.get_attr("getregentry", self)?;
278+
let codec_info = getregentry.call((), self)?;
279+
self.state
280+
.codec_registry
281+
.register_manual("ascii", codec_info.try_into_value(self)?)?;
282+
283+
// Register utf-8 encoding
284+
let utf8_module = import::import_frozen(self, utf8_module_name)?;
285+
let getregentry = utf8_module.get_attr("getregentry", self)?;
275286
let codec_info = getregentry.call((), self)?;
276287
self.state
277288
.codec_registry
@@ -298,7 +309,7 @@ impl VirtualMachine {
298309
#[cfg(not(feature = "threading"))]
299310
import::import_frozen(self, "_thread")?;
300311
let importlib = import::init_importlib_base(self)?;
301-
self.import_utf8_encodings()?;
312+
self.import_ascii_utf8_encodings()?;
302313

303314
#[cfg(any(not(target_arch = "wasm32"), target_os = "wasi"))]
304315
{
@@ -327,17 +338,25 @@ impl VirtualMachine {
327338
let line_buffering = buffered_stdio && (isatty || fd == 2);
328339

329340
let newline = if cfg!(windows) { None } else { Some("\n") };
330-
// stderr uses backslashreplace error handler
331-
let errors: Option<&str> = if fd == 2 {
341+
let encoding = self.state.config.settings.stdio_encoding.as_deref();
342+
// stderr always uses backslashreplace (ignores stdio_errors)
343+
let errors = if fd == 2 {
332344
Some("backslashreplace")
333345
} else {
334-
None
346+
self.state.config.settings.stdio_errors.as_deref()
335347
};
336348

337349
let stdio = self.call_method(
338350
&io,
339351
"TextIOWrapper",
340-
(buf, (), errors, newline, line_buffering, write_through),
352+
(
353+
buf,
354+
encoding,
355+
errors,
356+
newline,
357+
line_buffering,
358+
write_through,
359+
),
341360
)?;
342361
let mode = if write { "w" } else { "r" };
343362
stdio.set_attr("mode", self.ctx.new_str(mode), self)?;
@@ -1007,6 +1026,8 @@ pub fn resolve_frozen_alias(name: &str) -> &str {
10071026
match name {
10081027
"_frozen_importlib" => "importlib._bootstrap",
10091028
"_frozen_importlib_external" => "importlib._bootstrap_external",
1029+
"encodings_ascii" => "encodings.ascii",
1030+
"encodings_utf_8" => "encodings.utf_8",
10101031
_ => name,
10111032
}
10121033
}

crates/vm/src/vm/setting.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,11 @@ pub struct Settings {
112112
/// -u, PYTHONUNBUFFERED=x
113113
pub buffered_stdio: bool,
114114

115-
// wchar_t *stdio_encoding;
115+
/// PYTHONIOENCODING - stdio encoding
116+
pub stdio_encoding: Option<String>,
117+
/// PYTHONIOENCODING - stdio error handler
118+
pub stdio_errors: Option<String>,
116119
pub utf8_mode: u8,
117-
// wchar_t *stdio_errors;
118120
/// --check-hash-based-pycs
119121
pub check_hash_pycs_mode: CheckHashPycsMode,
120122

@@ -197,6 +199,8 @@ impl Default for Settings {
197199
buffered_stdio: true,
198200
check_hash_pycs_mode: CheckHashPycsMode::Default,
199201
allow_external_library: cfg!(feature = "importlib"),
202+
stdio_encoding: None,
203+
stdio_errors: None,
200204
utf8_mode: 1,
201205
int_max_str_digits: 4300,
202206
#[cfg(feature = "flame-it")]

src/settings.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,23 @@ pub fn parse_opts() -> Result<(Settings, RunMode), lexopt::Error> {
298298
settings.code_debug_ranges = false;
299299
}
300300

301+
// Parse PYTHONIOENCODING=encoding[:errors]
302+
if let Some(val) = get_env("PYTHONIOENCODING")
303+
&& let Some(val_str) = val.to_str()
304+
&& !val_str.is_empty()
305+
{
306+
if let Some((enc, err)) = val_str.split_once(':') {
307+
if !enc.is_empty() {
308+
settings.stdio_encoding = Some(enc.to_owned());
309+
}
310+
if !err.is_empty() {
311+
settings.stdio_errors = Some(err.to_owned());
312+
}
313+
} else {
314+
settings.stdio_encoding = Some(val_str.to_owned());
315+
}
316+
}
317+
301318
if settings.dev_mode {
302319
settings.warnoptions.push("default".to_owned());
303320
settings.faulthandler = true;

0 commit comments

Comments
 (0)