@@ -4187,6 +4187,37 @@ mod _io {
41874187 }
41884188 }
41894189
4190+ impl SeenNewline {
4191+ fn observe ( & mut self , text : & Wtf8 ) {
4192+ let bytes = text. as_bytes ( ) ;
4193+ let mut matches = memchr:: memchr2_iter ( b'\r' , b'\n' , bytes) ;
4194+ while !self . is_all ( ) {
4195+ let Some ( i) = matches. next ( ) else { break } ;
4196+ match bytes[ i] {
4197+ b'\n' => self . insert ( Self :: LF ) ,
4198+ _ if bytes. get ( i + 1 ) == Some ( & b'\n' ) => {
4199+ matches. next ( ) ;
4200+ self . insert ( Self :: CRLF ) ;
4201+ }
4202+ _ => self . insert ( Self :: CR ) ,
4203+ }
4204+ }
4205+ }
4206+
4207+ fn to_pyobject ( self , vm : & VirtualMachine ) -> PyObjectRef {
4208+ match self . bits ( ) {
4209+ 1 => "\n " . to_pyobject ( vm) ,
4210+ 2 => "\r " . to_pyobject ( vm) ,
4211+ 3 => ( "\r " , "\n " ) . to_pyobject ( vm) ,
4212+ 4 => "\r \n " . to_pyobject ( vm) ,
4213+ 5 => ( "\n " , "\r \n " ) . to_pyobject ( vm) ,
4214+ 6 => ( "\r " , "\r \n " ) . to_pyobject ( vm) ,
4215+ 7 => ( "\r " , "\n " , "\r \n " ) . to_pyobject ( vm) ,
4216+ _ => vm. ctx . none ( ) ,
4217+ }
4218+ }
4219+ }
4220+
41904221 impl DefaultConstructor for IncrementalNewlineDecoder { }
41914222
41924223 #[ derive( FromArgs ) ]
@@ -4278,16 +4309,7 @@ mod _io {
42784309 #[ pygetset]
42794310 fn newlines ( & self , vm : & VirtualMachine ) -> PyResult {
42804311 let data = self . lock ( vm) ?;
4281- Ok ( match data. seennl . bits ( ) {
4282- 1 => "\n " . to_pyobject ( vm) ,
4283- 2 => "\r " . to_pyobject ( vm) ,
4284- 3 => ( "\r " , "\n " ) . to_pyobject ( vm) ,
4285- 4 => "\r \n " . to_pyobject ( vm) ,
4286- 5 => ( "\n " , "\r \n " ) . to_pyobject ( vm) ,
4287- 6 => ( "\r " , "\r \n " ) . to_pyobject ( vm) ,
4288- 7 => ( "\r " , "\n " , "\r \n " ) . to_pyobject ( vm) ,
4289- _ => vm. ctx . none ( ) ,
4290- } )
4312+ Ok ( data. seennl . to_pyobject ( vm) )
42914313 }
42924314 }
42934315
@@ -4334,20 +4356,7 @@ mod _io {
43344356 self . seennl . insert ( SeenNewline :: LF ) ;
43354357 }
43364358 } else if !self . translate {
4337- let output = output. as_bytes ( ) ;
4338- let mut matches = memchr:: memchr2_iter ( b'\r' , b'\n' , output) ;
4339- while !self . seennl . is_all ( ) {
4340- let Some ( i) = matches. next ( ) else { break } ;
4341- match output[ i] {
4342- b'\n' => self . seennl . insert ( SeenNewline :: LF ) ,
4343- // if c isn't \n, it can only be \r
4344- _ if output. get ( i + 1 ) == Some ( & b'\n' ) => {
4345- matches. next ( ) ;
4346- self . seennl . insert ( SeenNewline :: CRLF ) ;
4347- }
4348- _ => self . seennl . insert ( SeenNewline :: CR ) ,
4349- }
4350- }
4359+ self . seennl . observe ( & output) ;
43514360 } else {
43524361 let bytes = output. as_bytes ( ) ;
43534362 let mut matches = memchr:: memchr2_iter ( b'\r' , b'\n' , bytes) ;
@@ -4390,6 +4399,7 @@ mod _io {
43904399 _base : _TextIOBase ,
43914400 buffer : PyRwLock < BufferedIO > ,
43924401 newline : AtomicCell < Newlines > ,
4402+ seennl : AtomicCell < SeenNewline > ,
43934403 closed : AtomicCell < bool > ,
43944404 }
43954405
@@ -4410,6 +4420,7 @@ mod _io {
44104420 _base : Default :: default ( ) ,
44114421 buffer : PyRwLock :: new ( BufferedIO :: new ( Cursor :: new ( Vec :: new ( ) ) ) ) ,
44124422 newline : AtomicCell :: new ( Newlines :: Lf ) ,
4423+ seennl : AtomicCell :: new ( SeenNewline :: empty ( ) ) ,
44134424 closed : AtomicCell :: new ( false ) ,
44144425 } )
44154426 }
@@ -4428,11 +4439,15 @@ mod _io {
44284439 OptionalArg :: Present ( None ) => Newlines :: Universal ,
44294440 OptionalArg :: Present ( Some ( newline) ) => newline,
44304441 } ;
4431- let raw_bytes = object. flatten ( ) . map_or_else ( Vec :: new, |v| {
4442+ let object = object. flatten ( ) ;
4443+ let raw_bytes = object. as_ref ( ) . map_or_else ( Vec :: new, |v| {
44324444 Self :: translate_newlines ( v. as_wtf8 ( ) , newline) . into_bytes ( )
44334445 } ) ;
44344446 * zelf. buffer . write ( ) = BufferedIO :: new ( Cursor :: new ( raw_bytes) ) ;
44354447 zelf. newline . store ( newline) ;
4448+ if let Some ( object) = object {
4449+ zelf. observe_newlines ( object. as_wtf8 ( ) , newline) ;
4450+ }
44364451 Ok ( ( ) )
44374452 }
44384453 }
@@ -4457,6 +4472,14 @@ mod _io {
44574472 }
44584473 }
44594474
4475+ fn observe_newlines ( & self , data : & Wtf8 , newline : Newlines ) {
4476+ if matches ! ( newline, Newlines :: Universal | Newlines :: Passthrough ) {
4477+ let mut seennl = self . seennl . load ( ) ;
4478+ seennl. observe ( data) ;
4479+ self . seennl . store ( seennl) ;
4480+ }
4481+ }
4482+
44604483 fn text ( bytes : & [ u8 ] ) -> & Wtf8 {
44614484 // SAFETY: StringIO is populated only from PyStr values, which are valid WTF-8.
44624485 unsafe { Wtf8 :: from_bytes_unchecked ( bytes) }
@@ -4509,6 +4532,11 @@ mod _io {
45094532 self . closed . load ( )
45104533 }
45114534
4535+ #[ pygetset]
4536+ fn newlines ( & self , vm : & VirtualMachine ) -> PyObjectRef {
4537+ self . seennl . load ( ) . to_pyobject ( vm)
4538+ }
4539+
45124540 #[ pymethod]
45134541 fn close ( & self ) {
45144542 self . closed . store ( true ) ;
@@ -4517,8 +4545,11 @@ mod _io {
45174545 // write string to underlying vector
45184546 #[ pymethod]
45194547 fn write ( & self , data : PyStrRef , vm : & VirtualMachine ) -> PyResult < u64 > {
4520- let bytes = Self :: translate_newlines ( data. as_wtf8 ( ) , self . newline . load ( ) ) . into_bytes ( ) ;
4521- self . buffer ( vm) ?
4548+ let newline = self . newline . load ( ) ;
4549+ let bytes = Self :: translate_newlines ( data. as_wtf8 ( ) , newline) . into_bytes ( ) ;
4550+ let mut buffer = self . buffer ( vm) ?;
4551+ self . observe_newlines ( data. as_wtf8 ( ) , newline) ;
4552+ buffer
45224553 . write ( & bytes)
45234554 . ok_or_else ( || vm. new_type_error ( "Error Writing String" ) ) ?;
45244555 Ok ( data. char_len ( ) as u64 )
@@ -4678,6 +4709,11 @@ mod _io {
46784709 . map_err ( |err| os_err ( vm, err) ) ?;
46794710 drop ( buffer) ;
46804711 zelf. newline . store ( newline) ;
4712+ let mut seennl = SeenNewline :: empty ( ) ;
4713+ if matches ! ( newline, Newlines :: Universal | Newlines :: Passthrough ) {
4714+ seennl. observe ( content. as_wtf8 ( ) ) ;
4715+ }
4716+ zelf. seennl . store ( seennl) ;
46814717
46824718 // Set __dict__ if provided
46834719 if !vm. is_none ( dict) {
0 commit comments