@@ -8,7 +8,7 @@ use std::io::{self, prelude::*, Cursor, SeekFrom};
8
8
use num_traits:: ToPrimitive ;
9
9
10
10
use crate :: exceptions:: PyBaseExceptionRef ;
11
- use crate :: function:: { OptionalArg , OptionalOption , PyFuncArgs } ;
11
+ use crate :: function:: { Args , KwArgs , OptionalArg , OptionalOption , PyFuncArgs } ;
12
12
use crate :: obj:: objbool;
13
13
use crate :: obj:: objbytearray:: PyByteArray ;
14
14
use crate :: obj:: objbyteinner:: PyBytesLike ;
@@ -577,35 +577,57 @@ mod fileio {
577
577
flag as u32
578
578
}
579
579
580
- fn file_io_init (
581
- file_io : PyObjectRef ,
580
+ #[ derive( FromArgs ) ]
581
+ struct FileIOArgs {
582
+ #[ pyarg( positional_only) ]
582
583
name : Either < PyStringRef , i64 > ,
583
- mode : OptionalArg < PyStringRef > ,
584
- vm : & VirtualMachine ,
585
- ) -> PyResult {
586
- let ( name, file_no) = match name {
584
+ #[ pyarg( positional_or_keyword, default = "None" ) ]
585
+ mode : Option < PyStringRef > ,
586
+ #[ pyarg( positional_or_keyword, default = "true" ) ]
587
+ closefd : bool ,
588
+ #[ pyarg( positional_or_keyword, default = "None" ) ]
589
+ opener : Option < PyObjectRef > ,
590
+ }
591
+ fn file_io_init ( file_io : PyObjectRef , args : FileIOArgs , vm : & VirtualMachine ) -> PyResult {
592
+ let ( name, file_no) = match args. name {
587
593
Either :: A ( name) => {
588
- let mode = match mode {
589
- OptionalArg :: Present ( mode) => compute_c_flag ( mode. as_str ( ) ) ,
590
- OptionalArg :: Missing => libc:: O_RDONLY as _ ,
594
+ if !args. closefd {
595
+ return Err (
596
+ vm. new_value_error ( "Cannot use closefd=False with file name" . to_owned ( ) )
597
+ ) ;
598
+ }
599
+ let mode = match args. mode {
600
+ Some ( mode) => compute_c_flag ( mode. as_str ( ) ) ,
601
+ None => libc:: O_RDONLY as _ ,
591
602
} ;
592
- (
593
- name. clone ( ) . into_object ( ) ,
603
+ let fd = if let Some ( opener) = args. opener {
604
+ let fd =
605
+ vm. invoke ( & opener, vec ! [ name. clone( ) . into_object( ) , vm. new_int( mode) ] ) ?;
606
+ if !vm. isinstance ( & fd, & vm. ctx . types . int_type ) ? {
607
+ return Err ( vm. new_type_error ( "expected integer from opener" . to_owned ( ) ) ) ;
608
+ }
609
+ let fd = i64:: try_from_object ( vm, fd) ?;
610
+ if fd < 0 {
611
+ return Err ( vm. new_os_error ( "Negative file descriptor" . to_owned ( ) ) ) ;
612
+ }
613
+ fd
614
+ } else {
594
615
os:: os_open (
595
616
os:: PyPathLike :: new_str ( name. as_str ( ) . to_owned ( ) ) ,
596
617
mode as _ ,
597
618
OptionalArg :: Missing ,
598
619
OptionalArg :: Missing ,
599
620
vm,
600
- ) ?,
601
- )
621
+ ) ?
622
+ } ;
623
+ ( name. clone ( ) . into_object ( ) , fd)
602
624
}
603
625
Either :: B ( fno) => ( vm. new_int ( fno) , fno) ,
604
626
} ;
605
627
606
628
vm. set_attr ( & file_io, "name" , name) ?;
607
629
vm. set_attr ( & file_io, "__fileno" , vm. new_int ( file_no) ) ?;
608
- vm. set_attr ( & file_io, "closefd " , vm. new_bool ( false ) ) ?;
630
+ vm. set_attr ( & file_io, "__closefd " , vm. new_bool ( args . closefd ) ) ?;
609
631
vm. set_attr ( & file_io, "__closed" , vm. new_bool ( false ) ) ?;
610
632
Ok ( vm. get_none ( ) )
611
633
}
@@ -701,9 +723,12 @@ mod fileio {
701
723
}
702
724
703
725
fn file_io_close ( instance : PyObjectRef , vm : & VirtualMachine ) -> PyResult < ( ) > {
704
- let raw_handle = i64:: try_from_object ( vm, vm. get_attribute ( instance. clone ( ) , "__fileno" ) ?) ?;
705
- drop ( os:: rust_file ( raw_handle) ) ;
706
- vm. set_attr ( & instance, "closefd" , vm. new_bool ( true ) ) ?;
726
+ let closefd = objbool:: boolval ( vm, vm. get_attribute ( instance. clone ( ) , "__closefd" ) ?) ?;
727
+ if closefd {
728
+ let raw_handle =
729
+ i64:: try_from_object ( vm, vm. get_attribute ( instance. clone ( ) , "__fileno" ) ?) ?;
730
+ drop ( os:: rust_file ( raw_handle) ) ;
731
+ }
707
732
vm. set_attr ( & instance, "__closed" , vm. new_bool ( true ) ) ?;
708
733
Ok ( ( ) )
709
734
}
@@ -949,16 +974,56 @@ fn split_mode_string(mode_string: &str) -> Result<(String, String), String> {
949
974
fn io_open_wrapper (
950
975
file : PyObjectRef ,
951
976
mode : OptionalArg < PyStringRef > ,
977
+ opts : OpenArgs ,
952
978
vm : & VirtualMachine ,
953
979
) -> PyResult {
954
- io_open ( file, mode. as_ref ( ) . into_option ( ) . map ( |s| s. as_str ( ) ) , vm)
980
+ io_open (
981
+ file,
982
+ mode. as_ref ( ) . into_option ( ) . map ( |s| s. as_str ( ) ) ,
983
+ opts,
984
+ vm,
985
+ )
955
986
}
956
987
fn io_open_code ( file : PyObjectRef , vm : & VirtualMachine ) -> PyResult {
957
988
// TODO: lifecycle hooks or something?
958
- io_open ( file, Some ( "rb" ) , vm)
989
+ io_open ( file, Some ( "rb" ) , Default :: default ( ) , vm)
990
+ }
991
+
992
+ #[ derive( FromArgs ) ]
993
+ #[ allow( unused) ]
994
+ pub struct OpenArgs {
995
+ #[ pyarg( positional_or_keyword, default = "-1" ) ]
996
+ buffering : isize ,
997
+ #[ pyarg( positional_or_keyword, default = "None" ) ]
998
+ encoding : Option < PyStringRef > ,
999
+ #[ pyarg( positional_or_keyword, default = "None" ) ]
1000
+ errors : Option < PyStringRef > ,
1001
+ #[ pyarg( positional_or_keyword, default = "None" ) ]
1002
+ newline : Option < PyStringRef > ,
1003
+ #[ pyarg( positional_or_keyword, default = "true" ) ]
1004
+ closefd : bool ,
1005
+ #[ pyarg( positional_or_keyword, default = "None" ) ]
1006
+ opener : Option < PyObjectRef > ,
1007
+ }
1008
+ impl Default for OpenArgs {
1009
+ fn default ( ) -> Self {
1010
+ OpenArgs {
1011
+ buffering : -1 ,
1012
+ encoding : None ,
1013
+ errors : None ,
1014
+ newline : None ,
1015
+ closefd : true ,
1016
+ opener : None ,
1017
+ }
1018
+ }
959
1019
}
960
1020
961
- pub fn io_open ( file : PyObjectRef , mode : Option < & str > , vm : & VirtualMachine ) -> PyResult {
1021
+ pub fn io_open (
1022
+ file : PyObjectRef ,
1023
+ mode : Option < & str > ,
1024
+ opts : OpenArgs ,
1025
+ vm : & VirtualMachine ,
1026
+ ) -> PyResult {
962
1027
// mode is optional: 'rt' is the default mode (open from reading text)
963
1028
let mode_string = mode. unwrap_or ( "rt" ) ;
964
1029
@@ -981,7 +1046,13 @@ pub fn io_open(file: PyObjectRef, mode: Option<&str>, vm: &VirtualMachine) -> Py
981
1046
} ) ?;
982
1047
let file_io_obj = vm. invoke (
983
1048
& file_io_class,
984
- vec ! [ file. clone( ) , vm. ctx. new_str( mode. clone( ) ) ] ,
1049
+ PyFuncArgs :: from ( (
1050
+ Args :: new ( vec ! [ file. clone( ) , vm. ctx. new_str( mode. clone( ) ) ] ) ,
1051
+ KwArgs :: new ( maplit:: hashmap! {
1052
+ "closefd" . to_owned( ) => vm. new_bool( opts. closefd) ,
1053
+ "opener" . to_owned( ) => opts. opener. unwrap_or_else( || vm. get_none( ) ) ,
1054
+ } ) ,
1055
+ ) ) ,
985
1056
) ?;
986
1057
987
1058
// Create Buffered class to consume FileIO. The type of buffered class depends on
0 commit comments