Skip to content

Commit 2981010

Browse files
authored
Merge pull request #7195 from youknowone/sys
StatelessIncrementalEncoder, sys attributes
2 parents f5eadae + d19d523 commit 2981010

File tree

6 files changed

+167
-25
lines changed

6 files changed

+167
-25
lines changed

Lib/test/test_compile.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1012,7 +1012,7 @@ def return_genexp():
10121012
code_lines = self.get_code_lines(genexp_code)
10131013
self.assertEqual(genexp_lines, code_lines)
10141014

1015-
# TODO: RUSTPYTHON
1015+
# TODO: RUSTPYTHON; implicit return line number after async for
10161016
@unittest.expectedFailure
10171017
def test_line_number_implicit_return_after_async_for(self):
10181018

Lib/test/test_sys.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,6 @@ def test_setrecursionlimit_to_depth(self):
401401
finally:
402402
sys.setrecursionlimit(old_limit)
403403

404-
@unittest.expectedFailure # TODO: RUSTPYTHON
405404
def test_getwindowsversion(self):
406405
# Raise SkipTest if sys doesn't have getwindowsversion attribute
407406
test.support.get_attribute(sys, "getwindowsversion")
@@ -851,7 +850,6 @@ def test_subinterp_intern_singleton(self):
851850
'''))
852851
self.assertTrue(sys._is_interned(s))
853852

854-
@unittest.expectedFailure # TODO: RUSTPYTHON; needs update for context_aware_warnings
855853
def test_sys_flags(self):
856854
self.assertTrue(sys.flags)
857855
attrs = ("debug",
@@ -880,7 +878,7 @@ def test_sys_flags_no_instantiation(self):
880878
def test_sys_version_info_no_instantiation(self):
881879
self.assert_raise_on_new_sys_type(sys.version_info)
882880

883-
@unittest.expectedFailure # TODO: RUSTPYTHON
881+
@unittest.expectedFailure # TODO: RUSTPYTHON; TypeError not raised for getwindowsversion instantiation
884882
def test_sys_getwindowsversion_no_instantiation(self):
885883
# Skip if not being run on Windows.
886884
test.support.get_attribute(sys, "getwindowsversion")
@@ -1057,12 +1055,12 @@ def check_locale_surrogateescape(self, locale):
10571055
'stdout: surrogateescape\n'
10581056
'stderr: backslashreplace\n')
10591057

1060-
@unittest.expectedFailure # TODO: RUSTPYTHON; stderr: backslashreplace
1058+
@unittest.expectedFailure # TODO: RUSTPYTHON; iso8859_1 codec not registered
10611059
@support.requires_subprocess()
10621060
def test_c_locale_surrogateescape(self):
10631061
self.check_locale_surrogateescape('C')
10641062

1065-
@unittest.expectedFailure # TODO: RUSTPYTHON; stderr: backslashreplace
1063+
@unittest.expectedFailure # TODO: RUSTPYTHON; iso8859_1 codec not registered
10661064
@support.requires_subprocess()
10671065
def test_posix_locale_surrogateescape(self):
10681066
self.check_locale_surrogateescape('POSIX')

crates/codegen/src/snapshots/rustpython_codegen__compile__tests__nested_double_async_with.snap

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/vm/src/stdlib/io.rs

Lines changed: 120 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2607,6 +2607,92 @@ mod _io {
26072607
}
26082608
}
26092609

2610+
#[pyclass(module = "_io", name, no_attr)]
2611+
#[derive(Debug, PyPayload)]
2612+
struct StatelessIncrementalEncoder {
2613+
encode: PyObjectRef,
2614+
errors: Option<PyStrRef>,
2615+
name: Option<PyStrRef>,
2616+
}
2617+
2618+
#[pyclass]
2619+
impl StatelessIncrementalEncoder {
2620+
#[pymethod]
2621+
fn encode(
2622+
&self,
2623+
input: PyObjectRef,
2624+
_final: OptionalArg<bool>,
2625+
vm: &VirtualMachine,
2626+
) -> PyResult {
2627+
let mut args: Vec<PyObjectRef> = vec![input];
2628+
if let Some(errors) = &self.errors {
2629+
args.push(errors.to_owned().into());
2630+
}
2631+
let res = self.encode.call(args, vm)?;
2632+
let tuple: PyTupleRef = res.try_into_value(vm)?;
2633+
if tuple.len() != 2 {
2634+
return Err(vm.new_type_error("encoder must return a tuple (object, integer)"));
2635+
}
2636+
Ok(tuple[0].clone())
2637+
}
2638+
2639+
#[pymethod]
2640+
fn reset(&self) {}
2641+
2642+
#[pymethod]
2643+
fn setstate(&self, _state: PyObjectRef) {}
2644+
2645+
#[pymethod]
2646+
fn getstate(&self, vm: &VirtualMachine) -> PyObjectRef {
2647+
vm.ctx.new_int(0).into()
2648+
}
2649+
2650+
#[pygetset]
2651+
fn name(&self) -> Option<PyStrRef> {
2652+
self.name.clone()
2653+
}
2654+
}
2655+
2656+
#[pyclass(module = "_io", name, no_attr)]
2657+
#[derive(Debug, PyPayload)]
2658+
struct StatelessIncrementalDecoder {
2659+
decode: PyObjectRef,
2660+
errors: Option<PyStrRef>,
2661+
}
2662+
2663+
#[pyclass]
2664+
impl StatelessIncrementalDecoder {
2665+
#[pymethod]
2666+
fn decode(
2667+
&self,
2668+
input: PyObjectRef,
2669+
_final: OptionalArg<bool>,
2670+
vm: &VirtualMachine,
2671+
) -> PyResult {
2672+
let mut args: Vec<PyObjectRef> = vec![input];
2673+
if let Some(errors) = &self.errors {
2674+
args.push(errors.to_owned().into());
2675+
}
2676+
let res = self.decode.call(args, vm)?;
2677+
let tuple: PyTupleRef = res.try_into_value(vm)?;
2678+
if tuple.len() != 2 {
2679+
return Err(vm.new_type_error("decoder must return a tuple (object, integer)"));
2680+
}
2681+
Ok(tuple[0].clone())
2682+
}
2683+
2684+
#[pymethod]
2685+
fn getstate(&self, vm: &VirtualMachine) -> (PyBytesRef, u64) {
2686+
(vm.ctx.empty_bytes.to_owned(), 0)
2687+
}
2688+
2689+
#[pymethod]
2690+
fn setstate(&self, _state: PyTupleRef, _vm: &VirtualMachine) {}
2691+
2692+
#[pymethod]
2693+
fn reset(&self) {}
2694+
}
2695+
26102696
#[pyattr]
26112697
#[pyclass(name = "TextIOWrapper", base = _TextIOBase)]
26122698
#[derive(Debug, Default)]
@@ -2830,7 +2916,25 @@ mod _io {
28302916

28312917
let encoder = if vm.call_method(buffer, "writable", ())?.try_to_bool(vm)? {
28322918
let incremental_encoder =
2833-
codec.get_incremental_encoder(Some(errors.to_owned()), vm)?;
2919+
match codec.get_incremental_encoder(Some(errors.to_owned()), vm) {
2920+
Ok(encoder) => encoder,
2921+
Err(err)
2922+
if err.fast_isinstance(vm.ctx.exceptions.type_error)
2923+
|| err.fast_isinstance(vm.ctx.exceptions.attribute_error) =>
2924+
{
2925+
let name = vm
2926+
.get_attribute_opt(codec.as_tuple().to_owned().into(), "name")?
2927+
.and_then(|obj| obj.downcast::<PyStr>().ok());
2928+
StatelessIncrementalEncoder {
2929+
encode: codec.get_encode_func().to_owned(),
2930+
errors: Some(errors.to_owned()),
2931+
name,
2932+
}
2933+
.into_ref(&vm.ctx)
2934+
.into()
2935+
}
2936+
Err(err) => return Err(err),
2937+
};
28342938
let encoding_name = vm.get_attribute_opt(incremental_encoder.clone(), "name")?;
28352939
let encode_func = encoding_name.and_then(|name| {
28362940
let name = name.downcast_ref::<PyStr>()?;
@@ -2845,7 +2949,21 @@ mod _io {
28452949
};
28462950

28472951
let decoder = if vm.call_method(buffer, "readable", ())?.try_to_bool(vm)? {
2848-
let decoder = codec.get_incremental_decoder(Some(errors.to_owned()), vm)?;
2952+
let decoder = match codec.get_incremental_decoder(Some(errors.to_owned()), vm) {
2953+
Ok(decoder) => decoder,
2954+
Err(err)
2955+
if err.fast_isinstance(vm.ctx.exceptions.type_error)
2956+
|| err.fast_isinstance(vm.ctx.exceptions.attribute_error) =>
2957+
{
2958+
StatelessIncrementalDecoder {
2959+
decode: codec.get_decode_func().to_owned(),
2960+
errors: Some(errors.to_owned()),
2961+
}
2962+
.into_ref(&vm.ctx)
2963+
.into()
2964+
}
2965+
Err(err) => return Err(err),
2966+
};
28492967
if let Newlines::Universal | Newlines::Passthrough = newline {
28502968
let args = IncrementalNewlineDecoderArgs {
28512969
decoder,

crates/vm/src/stdlib/sys.rs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ mod sys {
7373
RUST_MULTIARCH.replace("-unknown", "")
7474
}
7575

76-
#[pyclass(no_attr, name = "_BootstrapStderr", module = "sys")]
76+
#[pyclass(no_attr, name = "_BootstrapStderr")]
7777
#[derive(Debug, PyPayload)]
7878
pub(super) struct BootstrapStderr;
7979

@@ -95,7 +95,7 @@ mod sys {
9595

9696
/// Lightweight stdio wrapper for sandbox mode (no host_env).
9797
/// Directly uses Rust's std::io for stdin/stdout/stderr without FileIO.
98-
#[pyclass(no_attr, name = "_SandboxStdio", module = "sys")]
98+
#[pyclass(no_attr, name = "_SandboxStdio")]
9999
#[derive(Debug, PyPayload)]
100100
pub struct SandboxStdio {
101101
pub fd: i32,
@@ -1565,10 +1565,6 @@ mod sys {
15651565
safe_path: bool,
15661566
/// -X warn_default_encoding, PYTHONWARNDEFAULTENCODING
15671567
warn_default_encoding: u8,
1568-
/// -X thread_inherit_context, whether new threads inherit context from parent
1569-
thread_inherit_context: bool,
1570-
/// -X context_aware_warnings, whether warnings are context aware
1571-
context_aware_warnings: bool,
15721568
}
15731569

15741570
impl FlagsData {
@@ -1592,13 +1588,11 @@ mod sys {
15921588
int_max_str_digits: settings.int_max_str_digits,
15931589
safe_path: settings.safe_path,
15941590
warn_default_encoding: settings.warn_default_encoding as u8,
1595-
thread_inherit_context: settings.thread_inherit_context,
1596-
context_aware_warnings: settings.context_aware_warnings,
15971591
}
15981592
}
15991593
}
16001594

1601-
#[pystruct_sequence(name = "flags", module = "sys", data = "FlagsData", no_attr)]
1595+
#[pystruct_sequence(name = "flags", data = "FlagsData", no_attr)]
16021596
pub(super) struct PyFlags;
16031597

16041598
#[pyclass(with(PyStructSequence))]
@@ -1607,6 +1601,16 @@ mod sys {
16071601
fn slot_new(_cls: PyTypeRef, _args: FuncArgs, vm: &VirtualMachine) -> PyResult {
16081602
Err(vm.new_type_error("cannot create 'sys.flags' instances"))
16091603
}
1604+
1605+
#[pygetset]
1606+
fn context_aware_warnings(&self, vm: &VirtualMachine) -> bool {
1607+
vm.state.config.settings.context_aware_warnings
1608+
}
1609+
1610+
#[pygetset]
1611+
fn thread_inherit_context(&self, vm: &VirtualMachine) -> bool {
1612+
vm.state.config.settings.thread_inherit_context
1613+
}
16101614
}
16111615

16121616
#[cfg(feature = "threading")]
@@ -1776,10 +1780,15 @@ mod sys {
17761780
build: u32,
17771781
platform: u32,
17781782
service_pack: String,
1783+
#[pystruct_sequence(skip)]
17791784
service_pack_major: u16,
1785+
#[pystruct_sequence(skip)]
17801786
service_pack_minor: u16,
1787+
#[pystruct_sequence(skip)]
17811788
suite_mask: u16,
1789+
#[pystruct_sequence(skip)]
17821790
product_type: u8,
1791+
#[pystruct_sequence(skip)]
17831792
platform_version: (u32, u32, u32),
17841793
}
17851794

crates/vm/src/vm/mod.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,21 @@ impl VirtualMachine {
331331
self.state
332332
.codec_registry
333333
.register_manual("utf8", utf8_codec)?;
334+
335+
// Register latin-1 / iso8859-1 aliases needed very early for stdio
336+
// bootstrap (e.g. PYTHONIOENCODING=latin-1).
337+
if cfg!(feature = "freeze-stdlib") {
338+
self.import("encodings.latin_1", 0)?;
339+
let latin1_module = sys_modules.get_item("encodings.latin_1", self)?;
340+
let getregentry = latin1_module.get_attr("getregentry", self)?;
341+
let codec_info = getregentry.call((), self)?;
342+
let latin1_codec: crate::codecs::PyCodec = codec_info.try_into_value(self)?;
343+
for name in ["latin-1", "latin_1", "latin1", "iso8859-1", "iso8859_1"] {
344+
self.state
345+
.codec_registry
346+
.register_manual(name, latin1_codec.clone())?;
347+
}
348+
}
334349
Ok(())
335350
}
336351

@@ -367,8 +382,8 @@ impl VirtualMachine {
367382
let io = import::import_builtin(self, "_io")?;
368383

369384
// Full stdio: FileIO → BufferedWriter → TextIOWrapper
370-
#[cfg(feature = "host_env")]
371-
let make_stdio = |name: &str, fd: i32, write: bool| {
385+
#[cfg(all(feature = "host_env", feature = "stdio"))]
386+
let make_stdio = |name: &str, fd: i32, write: bool| -> PyResult<PyObjectRef> {
372387
let buffered_stdio = self.state.config.settings.buffered_stdio;
373388
let unbuffered = write && !buffered_stdio;
374389
let buf = crate::stdlib::io::open(
@@ -397,12 +412,13 @@ impl VirtualMachine {
397412
let errors = if fd == 2 {
398413
Some("backslashreplace")
399414
} else {
400-
self.state
401-
.config
402-
.settings
403-
.stdio_errors
404-
.as_deref()
405-
.or(Some("surrogateescape"))
415+
self.state.config.settings.stdio_errors.as_deref().or(
416+
if self.state.config.settings.stdio_encoding.is_some() {
417+
Some("strict")
418+
} else {
419+
Some("surrogateescape")
420+
},
421+
)
406422
};
407423

408424
let stdio = self.call_method(

0 commit comments

Comments
 (0)