Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Fix more test_io
  • Loading branch information
youknowone committed Dec 28, 2025
commit f19db85e7d166e5fcab6a066cc5e0076dd978b85
8 changes: 0 additions & 8 deletions Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -1843,10 +1843,6 @@ def test_bad_readinto_type(self):
def test_error_through_destructor(self):
return super().test_error_through_destructor()

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_seek_character_device_file(self):
return super().test_seek_character_device_file()

def test_truncate_on_read_only(self):
return super().test_truncate_on_read_only()

Expand Down Expand Up @@ -2699,10 +2695,6 @@ def test_args_error(self):
def test_error_through_destructor(self):
return super().test_error_through_destructor()

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_seek_character_device_file(self):
return super().test_seek_character_device_file()

@unittest.skip('TODO: RUSTPYTHON; fallible allocation')
def test_constructor(self):
return super().test_constructor()
Expand Down
47 changes: 40 additions & 7 deletions crates/derive-impl/src/pystructseq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -316,20 +316,53 @@ pub(crate) fn impl_pystruct_sequence_data(

// Generate try_from_elements trait override only when try_from_object=true
let try_from_elements_trait_override = if try_from_object {
let visible_field_idents: Vec<_> = visible_fields.iter().map(|f| &f.ident).collect();
let skipped_field_idents: Vec<_> = skipped_fields.iter().map(|f| &f.ident).collect();
let visible_field_inits: Vec<_> = visible_fields
.iter()
.map(|f| {
let ident = &f.ident;
let cfg_attrs = &f.cfg_attrs;
if cfg_attrs.is_empty() {
quote! { #ident: iter.next().unwrap().clone().try_into_value(vm)?, }
} else {
quote! {
#(#cfg_attrs)*
#ident: iter.next().unwrap().clone().try_into_value(vm)?,
}
}
})
.collect();
let skipped_field_inits: Vec<_> = skipped_fields
.iter()
.map(|f| {
let ident = &f.ident;
let cfg_attrs = &f.cfg_attrs;
if cfg_attrs.is_empty() {
quote! {
#ident: match iter.next() {
Some(v) => v.clone().try_into_value(vm)?,
None => vm.ctx.none(),
},
}
} else {
quote! {
#(#cfg_attrs)*
#ident: match iter.next() {
Some(v) => v.clone().try_into_value(vm)?,
None => vm.ctx.none(),
},
}
}
})
.collect();
quote! {
fn try_from_elements(
elements: Vec<::rustpython_vm::PyObjectRef>,
vm: &::rustpython_vm::VirtualMachine,
) -> ::rustpython_vm::PyResult<Self> {
let mut iter = elements.into_iter();
Ok(Self {
#(#visible_field_idents: iter.next().unwrap().clone().try_into_value(vm)?,)*
#(#skipped_field_idents: match iter.next() {
Some(v) => v.clone().try_into_value(vm)?,
None => vm.ctx.none(),
},)*
#(#visible_field_inits)*
#(#skipped_field_inits)*
})
}
}
Expand Down
54 changes: 24 additions & 30 deletions crates/vm/src/stdlib/io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,10 @@ mod _io {
};
if offset >= -self.pos && offset <= available {
self.pos += offset;
return Ok(current - available + offset);
// GH-95782: character devices may report raw position 0
// even after reading, which would make this negative
let result = current - available + offset;
return Ok(if result < 0 { 0 } else { result });
}
}
}
Expand Down Expand Up @@ -1112,7 +1115,7 @@ mod _io {
}
}

// Handle flush errors like CPython: if BlockingIOError, shift buffer
// if BlockingIOError, shift buffer
// and try to buffer the new data; otherwise propagate the error
match self.flush(vm) {
Ok(()) => {}
Expand Down Expand Up @@ -1147,7 +1150,7 @@ mod _io {
return Err(vm.invoke_exception(
vm.ctx.exceptions.blocking_io_error.to_owned(),
vec![
vm.new_pyobj(libc::EAGAIN),
vm.new_pyobj(EAGAIN),
vm.new_pyobj("write could not complete without blocking"),
vm.new_pyobj(avail),
],
Expand Down Expand Up @@ -1738,34 +1741,25 @@ mod _io {
Ok(self.lock(vm)?.raw.clone())
}

/// Get raw stream without holding the lock (for calling Python code safely)
fn get_raw_unlocked(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
let data = self.lock(vm)?;
Ok(data.check_init(vm)?.to_owned())
}

#[pygetset]
fn closed(&self, vm: &VirtualMachine) -> PyResult {
// Don't hold the lock while calling Python code to avoid reentrant lock issues
let raw = {
let data = self.lock(vm)?;
data.check_init(vm)?.to_owned()
};
raw.get_attr("closed", vm)
self.get_raw_unlocked(vm)?.get_attr("closed", vm)
}

#[pygetset]
fn name(&self, vm: &VirtualMachine) -> PyResult {
// Don't hold the lock while calling Python code to avoid reentrant lock issues
let raw = {
let data = self.lock(vm)?;
data.check_init(vm)?.to_owned()
};
raw.get_attr("name", vm)
self.get_raw_unlocked(vm)?.get_attr("name", vm)
}

#[pygetset]
fn mode(&self, vm: &VirtualMachine) -> PyResult {
// Don't hold the lock while calling Python code to avoid reentrant lock issues
let raw = {
let data = self.lock(vm)?;
data.check_init(vm)?.to_owned()
};
raw.get_attr("mode", vm)
self.get_raw_unlocked(vm)?.get_attr("mode", vm)
}

#[pymethod]
Expand Down Expand Up @@ -1835,10 +1829,9 @@ mod _io {
Self::WRITABLE
}

// TODO: this should be the default for an equivalent of _PyObject_GetState
#[pymethod]
fn __reduce__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!("cannot pickle '{}' object", zelf.class().name())))
fn __getstate__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!("cannot pickle '{}' instances", zelf.class().name())))
}
}

Expand Down Expand Up @@ -2006,9 +1999,10 @@ mod _io {
#[pymethod]
fn write(&self, obj: ArgBytesLike, vm: &VirtualMachine) -> PyResult<usize> {
// Check if close() is in progress (Issue #31976)
// If closing, wait for close() to complete by spinning until raw is closed
// If closing, wait for close() to complete by spinning until raw is closed.
// Note: This spin-wait has no timeout because close() is expected to always
// complete (flush + fd close).
if self.writer().closing().load(Ordering::Acquire) {
// Wait for close() to complete - loop until raw.closed is True
loop {
let raw = {
let data = self.writer().lock(vm)?;
Expand Down Expand Up @@ -3460,8 +3454,8 @@ mod _io {
}

#[pymethod]
fn __reduce__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!("cannot pickle '{}' object", zelf.class().name())))
fn __getstate__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!("cannot pickle '{}' instances", zelf.class().name())))
}
}

Expand Down Expand Up @@ -5199,8 +5193,8 @@ mod fileio {
}

#[pymethod]
fn __reduce__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!("cannot pickle '{}' object", zelf.class().name())))
fn __getstate__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Err(vm.new_type_error(format!("cannot pickle '{}' instances", zelf.class().name())))
}
}

Expand Down
Loading