@@ -442,19 +442,62 @@ fn text_io_wrapper_init(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
442
442
fn text_io_base_read ( vm : & VirtualMachine , args : PyFuncArgs ) -> PyResult {
443
443
arg_check ! ( vm, args, required = [ ( text_io_base, None ) ] ) ;
444
444
445
+ let buffered_reader_class = vm. try_class ( "_io" , "BufferedReader" ) ?;
445
446
let raw = vm. get_attribute ( text_io_base. clone ( ) , "buffer" ) . unwrap ( ) ;
446
447
448
+ if !objtype:: isinstance ( & raw , & buffered_reader_class) {
449
+ // TODO: this should be io.UnsupportedOperation error which derives both from ValueError *and* OSError
450
+ return Err ( vm. new_value_error ( "not readable" . to_string ( ) ) ) ;
451
+ }
452
+
447
453
if let Ok ( bytes) = vm. call_method ( & raw , "read" , PyFuncArgs :: default ( ) ) {
448
454
let value = objbytes:: get_value ( & bytes) . to_vec ( ) ;
449
455
450
456
//format bytes into string
451
- let rust_string = String :: from_utf8 ( value) . unwrap ( ) ;
457
+ let rust_string = String :: from_utf8 ( value) . map_err ( |e| {
458
+ vm. new_unicode_decode_error ( format ! (
459
+ "cannot decode byte at index: {}" ,
460
+ e. utf8_error( ) . valid_up_to( )
461
+ ) )
462
+ } ) ?;
452
463
Ok ( vm. ctx . new_str ( rust_string) )
453
464
} else {
454
465
Err ( vm. new_value_error ( "Error unpacking Bytes" . to_string ( ) ) )
455
466
}
456
467
}
457
468
469
+ fn text_io_base_write ( vm : & VirtualMachine , args : PyFuncArgs ) -> PyResult {
470
+ use std:: str:: from_utf8;
471
+
472
+ arg_check ! (
473
+ vm,
474
+ args,
475
+ required = [ ( text_io_base, None ) , ( obj, Some ( vm. ctx. str_type( ) ) ) ]
476
+ ) ;
477
+
478
+ let buffered_writer_class = vm. try_class ( "_io" , "BufferedWriter" ) ?;
479
+ let raw = vm. get_attribute ( text_io_base. clone ( ) , "buffer" ) . unwrap ( ) ;
480
+
481
+ if !objtype:: isinstance ( & raw , & buffered_writer_class) {
482
+ // TODO: this should be io.UnsupportedOperation error which derives from ValueError and OSError
483
+ return Err ( vm. new_value_error ( "not writable" . to_string ( ) ) ) ;
484
+ }
485
+
486
+ let bytes = objstr:: get_value ( obj) . into_bytes ( ) ;
487
+
488
+ let len = vm. call_method ( & raw , "write" , vec ! [ vm. ctx. new_bytes( bytes. clone( ) ) ] ) ?;
489
+ let len = objint:: get_value ( & len) . to_usize ( ) . ok_or_else ( || {
490
+ vm. new_overflow_error ( "int to large to convert to Rust usize" . to_string ( ) )
491
+ } ) ?;
492
+
493
+ // returns the count of unicode code points written
494
+ let len = from_utf8 ( & bytes[ ..len] )
495
+ . unwrap_or_else ( |e| from_utf8 ( & bytes[ ..e. valid_up_to ( ) ] ) . unwrap ( ) )
496
+ . chars ( )
497
+ . count ( ) ;
498
+ Ok ( vm. ctx . new_int ( len) )
499
+ }
500
+
458
501
fn split_mode_string ( mode_string : String ) -> Result < ( String , String ) , String > {
459
502
let mut mode: char = '\0' ;
460
503
let mut typ: char = '\0' ;
@@ -594,7 +637,8 @@ pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
594
637
595
638
//TextIO Base has no public constructor
596
639
let text_io_base = py_class ! ( ctx, "TextIOBase" , io_base. clone( ) , {
597
- "read" => ctx. new_rustfunc( text_io_base_read)
640
+ "read" => ctx. new_rustfunc( text_io_base_read) ,
641
+ "write" => ctx. new_rustfunc( text_io_base_write)
598
642
} ) ;
599
643
600
644
// RawBaseIO Subclasses
0 commit comments