Skip to content

Commit f19db85

Browse files
committed
Fix more test_io
1 parent c6f2e73 commit f19db85

File tree

3 files changed

+64
-45
lines changed

3 files changed

+64
-45
lines changed

Lib/test/test_io.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1843,10 +1843,6 @@ def test_bad_readinto_type(self):
18431843
def test_error_through_destructor(self):
18441844
return super().test_error_through_destructor()
18451845

1846-
@unittest.expectedFailure # TODO: RUSTPYTHON
1847-
def test_seek_character_device_file(self):
1848-
return super().test_seek_character_device_file()
1849-
18501846
def test_truncate_on_read_only(self):
18511847
return super().test_truncate_on_read_only()
18521848

@@ -2699,10 +2695,6 @@ def test_args_error(self):
26992695
def test_error_through_destructor(self):
27002696
return super().test_error_through_destructor()
27012697

2702-
@unittest.expectedFailure # TODO: RUSTPYTHON
2703-
def test_seek_character_device_file(self):
2704-
return super().test_seek_character_device_file()
2705-
27062698
@unittest.skip('TODO: RUSTPYTHON; fallible allocation')
27072699
def test_constructor(self):
27082700
return super().test_constructor()

crates/derive-impl/src/pystructseq.rs

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -316,20 +316,53 @@ pub(crate) fn impl_pystruct_sequence_data(
316316

317317
// Generate try_from_elements trait override only when try_from_object=true
318318
let try_from_elements_trait_override = if try_from_object {
319-
let visible_field_idents: Vec<_> = visible_fields.iter().map(|f| &f.ident).collect();
320-
let skipped_field_idents: Vec<_> = skipped_fields.iter().map(|f| &f.ident).collect();
319+
let visible_field_inits: Vec<_> = visible_fields
320+
.iter()
321+
.map(|f| {
322+
let ident = &f.ident;
323+
let cfg_attrs = &f.cfg_attrs;
324+
if cfg_attrs.is_empty() {
325+
quote! { #ident: iter.next().unwrap().clone().try_into_value(vm)?, }
326+
} else {
327+
quote! {
328+
#(#cfg_attrs)*
329+
#ident: iter.next().unwrap().clone().try_into_value(vm)?,
330+
}
331+
}
332+
})
333+
.collect();
334+
let skipped_field_inits: Vec<_> = skipped_fields
335+
.iter()
336+
.map(|f| {
337+
let ident = &f.ident;
338+
let cfg_attrs = &f.cfg_attrs;
339+
if cfg_attrs.is_empty() {
340+
quote! {
341+
#ident: match iter.next() {
342+
Some(v) => v.clone().try_into_value(vm)?,
343+
None => vm.ctx.none(),
344+
},
345+
}
346+
} else {
347+
quote! {
348+
#(#cfg_attrs)*
349+
#ident: match iter.next() {
350+
Some(v) => v.clone().try_into_value(vm)?,
351+
None => vm.ctx.none(),
352+
},
353+
}
354+
}
355+
})
356+
.collect();
321357
quote! {
322358
fn try_from_elements(
323359
elements: Vec<::rustpython_vm::PyObjectRef>,
324360
vm: &::rustpython_vm::VirtualMachine,
325361
) -> ::rustpython_vm::PyResult<Self> {
326362
let mut iter = elements.into_iter();
327363
Ok(Self {
328-
#(#visible_field_idents: iter.next().unwrap().clone().try_into_value(vm)?,)*
329-
#(#skipped_field_idents: match iter.next() {
330-
Some(v) => v.clone().try_into_value(vm)?,
331-
None => vm.ctx.none(),
332-
},)*
364+
#(#visible_field_inits)*
365+
#(#skipped_field_inits)*
333366
})
334367
}
335368
}

crates/vm/src/stdlib/io.rs

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,7 +1000,10 @@ mod _io {
10001000
};
10011001
if offset >= -self.pos && offset <= available {
10021002
self.pos += offset;
1003-
return Ok(current - available + offset);
1003+
// GH-95782: character devices may report raw position 0
1004+
// even after reading, which would make this negative
1005+
let result = current - available + offset;
1006+
return Ok(if result < 0 { 0 } else { result });
10041007
}
10051008
}
10061009
}
@@ -1112,7 +1115,7 @@ mod _io {
11121115
}
11131116
}
11141117

1115-
// Handle flush errors like CPython: if BlockingIOError, shift buffer
1118+
// if BlockingIOError, shift buffer
11161119
// and try to buffer the new data; otherwise propagate the error
11171120
match self.flush(vm) {
11181121
Ok(()) => {}
@@ -1147,7 +1150,7 @@ mod _io {
11471150
return Err(vm.invoke_exception(
11481151
vm.ctx.exceptions.blocking_io_error.to_owned(),
11491152
vec![
1150-
vm.new_pyobj(libc::EAGAIN),
1153+
vm.new_pyobj(EAGAIN),
11511154
vm.new_pyobj("write could not complete without blocking"),
11521155
vm.new_pyobj(avail),
11531156
],
@@ -1738,34 +1741,25 @@ mod _io {
17381741
Ok(self.lock(vm)?.raw.clone())
17391742
}
17401743

1744+
/// Get raw stream without holding the lock (for calling Python code safely)
1745+
fn get_raw_unlocked(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
1746+
let data = self.lock(vm)?;
1747+
Ok(data.check_init(vm)?.to_owned())
1748+
}
1749+
17411750
#[pygetset]
17421751
fn closed(&self, vm: &VirtualMachine) -> PyResult {
1743-
// Don't hold the lock while calling Python code to avoid reentrant lock issues
1744-
let raw = {
1745-
let data = self.lock(vm)?;
1746-
data.check_init(vm)?.to_owned()
1747-
};
1748-
raw.get_attr("closed", vm)
1752+
self.get_raw_unlocked(vm)?.get_attr("closed", vm)
17491753
}
17501754

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

17611760
#[pygetset]
17621761
fn mode(&self, vm: &VirtualMachine) -> PyResult {
1763-
// Don't hold the lock while calling Python code to avoid reentrant lock issues
1764-
let raw = {
1765-
let data = self.lock(vm)?;
1766-
data.check_init(vm)?.to_owned()
1767-
};
1768-
raw.get_attr("mode", vm)
1762+
self.get_raw_unlocked(vm)?.get_attr("mode", vm)
17691763
}
17701764

17711765
#[pymethod]
@@ -1835,10 +1829,9 @@ mod _io {
18351829
Self::WRITABLE
18361830
}
18371831

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

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

34623456
#[pymethod]
3463-
fn __reduce__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
3464-
Err(vm.new_type_error(format!("cannot pickle '{}' object", zelf.class().name())))
3457+
fn __getstate__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
3458+
Err(vm.new_type_error(format!("cannot pickle '{}' instances", zelf.class().name())))
34653459
}
34663460
}
34673461

@@ -5199,8 +5193,8 @@ mod fileio {
51995193
}
52005194

52015195
#[pymethod]
5202-
fn __reduce__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
5203-
Err(vm.new_type_error(format!("cannot pickle '{}' object", zelf.class().name())))
5196+
fn __getstate__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult {
5197+
Err(vm.new_type_error(format!("cannot pickle '{}' instances", zelf.class().name())))
52045198
}
52055199
}
52065200

0 commit comments

Comments
 (0)