@@ -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