@@ -4393,6 +4393,12 @@ mod _io {
43934393 }
43944394 }
43954395
4396+ #[ derive( FromArgs ) ]
4397+ struct BytesIOArgs {
4398+ #[ pyarg( any, optional) ]
4399+ initial_bytes : OptionalArg < Option < ArgBytesLike > > ,
4400+ }
4401+
43964402 #[ pyattr]
43974403 #[ pyclass( name = "BytesIO" , base = _BufferedIOBase) ]
43984404 #[ derive( Debug ) ]
@@ -4417,15 +4423,17 @@ mod _io {
44174423 }
44184424
44194425 impl Initializer for BytesIO {
4420- type Args = OptionalArg < Option < ArgBytesLike > > ;
4426+ type Args = BytesIOArgs ;
44214427
4422- fn init ( zelf : PyRef < Self > , object : Self :: Args , vm : & VirtualMachine ) -> PyResult < ( ) > {
4428+ fn init ( zelf : PyRef < Self > , args : Self :: Args , vm : & VirtualMachine ) -> PyResult < ( ) > {
44234429 if zelf. exports . load ( ) > 0 {
44244430 return Err ( vm. new_buffer_error (
44254431 "Existing exports of data: object cannot be re-sized" . to_owned ( ) ,
44264432 ) ) ;
44274433 }
4428- let raw_bytes = object
4434+
4435+ let raw_bytes = args
4436+ . initial_bytes
44294437 . flatten ( )
44304438 . map_or_else ( Vec :: new, |input| input. borrow_buf ( ) . to_vec ( ) ) ;
44314439 * zelf. buffer . write ( ) = BufferedIO :: new ( Cursor :: new ( raw_bytes) ) ;
@@ -4503,9 +4511,20 @@ mod _io {
45034511 how : OptionalArg < i32 > ,
45044512 vm : & VirtualMachine ,
45054513 ) -> PyResult < u64 > {
4506- self . buffer ( vm) ?
4507- . seek ( seekfrom ( vm, offset, how) ?)
4508- . map_err ( |err| os_err ( vm, err) )
4514+ let seek_from = seekfrom ( vm, offset, how) ?;
4515+ let mut buffer = self . buffer ( vm) ?;
4516+
4517+ // Handle negative positions by clamping to 0
4518+ match seek_from {
4519+ SeekFrom :: Current ( offset) if offset < 0 => {
4520+ let current = buffer. tell ( ) ;
4521+ let new_pos = current. saturating_add_signed ( offset) ;
4522+ buffer
4523+ . seek ( SeekFrom :: Start ( new_pos) )
4524+ . map_err ( |err| os_err ( vm, err) )
4525+ }
4526+ _ => buffer. seek ( seek_from) . map_err ( |err| os_err ( vm, err) ) ,
4527+ }
45094528 }
45104529
45114530 #[ pymethod]
@@ -4534,8 +4553,14 @@ mod _io {
45344553 }
45354554
45364555 #[ pymethod]
4537- fn close ( & self ) {
4556+ fn close ( & self , vm : & VirtualMachine ) -> PyResult < ( ) > {
4557+ if self . exports . load ( ) > 0 {
4558+ return Err ( vm. new_buffer_error (
4559+ "Existing exports of data: object cannot be closed" . to_owned ( ) ,
4560+ ) ) ;
4561+ }
45384562 self . closed . store ( true ) ;
4563+ Ok ( ( ) )
45394564 }
45404565
45414566 #[ pymethod]
@@ -4602,6 +4627,9 @@ mod _io {
46024627 impl PyRef < BytesIO > {
46034628 #[ pymethod]
46044629 fn getbuffer ( self , vm : & VirtualMachine ) -> PyResult < PyMemoryView > {
4630+ if self . closed . load ( ) {
4631+ return Err ( vm. new_value_error ( "I/O operation on closed file." . to_owned ( ) ) ) ;
4632+ }
46054633 let len = self . buffer . read ( ) . cursor . get_ref ( ) . len ( ) ;
46064634 let buffer = PyBuffer :: new (
46074635 self . into ( ) ,
@@ -4931,14 +4959,19 @@ mod _io {
49314959 }
49324960
49334961 fn create_unsupported_operation ( ctx : & Context ) -> PyTypeRef {
4962+ use crate :: builtins:: type_:: PyAttributes ;
49344963 use crate :: types:: PyTypeSlots ;
4964+
4965+ let mut attrs = PyAttributes :: default ( ) ;
4966+ attrs. insert ( identifier ! ( ctx, __module__) , ctx. new_str ( "io" ) . into ( ) ) ;
4967+
49354968 PyType :: new_heap (
49364969 "UnsupportedOperation" ,
49374970 vec ! [
49384971 ctx. exceptions. os_error. to_owned( ) ,
49394972 ctx. exceptions. value_error. to_owned( ) ,
49404973 ] ,
4941- Default :: default ( ) ,
4974+ attrs ,
49424975 PyTypeSlots :: heap_default ( ) ,
49434976 ctx. types . type_type . to_owned ( ) ,
49444977 ctx,
0 commit comments