@@ -73,7 +73,7 @@ pub type PyObjectWeakRef = Weak<RefCell<PyObject>>;
73
73
/// Use this type for function which return a python object or and exception.
74
74
/// Both the python object and the python exception are `PyObjectRef` types
75
75
/// since exceptions are also python objects.
76
- pub type PyResult = Result < PyObjectRef , PyObjectRef > ; // A valid value, or an exception
76
+ pub type PyResult < T = PyObjectRef > = Result < T , PyObjectRef > ; // A valid value, or an exception
77
77
78
78
/// For attributes we do not use a dict, but a hashmap. This is probably
79
79
/// faster, unordered, and only supports strings as keys.
@@ -554,13 +554,13 @@ impl PyContext {
554
554
)
555
555
}
556
556
557
- pub fn new_rustfunc < F : ' static + Fn ( & mut VirtualMachine , PyFuncArgs ) -> PyResult > (
558
- & self ,
559
- function : F ,
560
- ) -> PyObjectRef {
557
+ pub fn new_rustfunc < F , T , R > ( & self , factory : F ) -> PyObjectRef
558
+ where
559
+ F : PyNativeFuncFactory < T , R > ,
560
+ {
561
561
PyObject :: new (
562
562
PyObjectPayload :: RustFunction {
563
- function : Box :: new ( function ) ,
563
+ function : factory . create ( self ) ,
564
564
} ,
565
565
self . builtin_function_or_method_type ( ) ,
566
566
)
@@ -946,6 +946,203 @@ impl PyFuncArgs {
946
946
}
947
947
}
948
948
949
+ pub trait FromPyObject : Sized {
950
+ fn typ ( ctx : & PyContext ) -> Option < PyObjectRef > ;
951
+
952
+ fn from_pyobject ( obj : PyObjectRef ) -> PyResult < Self > ;
953
+ }
954
+
955
+ impl FromPyObject for PyObjectRef {
956
+ fn typ ( _ctx : & PyContext ) -> Option < PyObjectRef > {
957
+ None
958
+ }
959
+
960
+ fn from_pyobject ( obj : PyObjectRef ) -> PyResult < Self > {
961
+ Ok ( obj)
962
+ }
963
+ }
964
+
965
+ pub trait IntoPyObject {
966
+ fn into_pyobject ( self , ctx : & PyContext ) -> PyResult ;
967
+ }
968
+
969
+ impl IntoPyObject for PyObjectRef {
970
+ fn into_pyobject ( self , _ctx : & PyContext ) -> PyResult {
971
+ Ok ( self )
972
+ }
973
+ }
974
+
975
+ impl IntoPyObject for PyResult {
976
+ fn into_pyobject ( self , _ctx : & PyContext ) -> PyResult {
977
+ self
978
+ }
979
+ }
980
+
981
+ pub trait FromPyFuncArgs : Sized {
982
+ fn required_params ( ctx : & PyContext ) -> Vec < Parameter > ;
983
+
984
+ fn from_py_func_args ( args : & mut PyFuncArgs ) -> PyResult < Self > ;
985
+ }
986
+
987
+ macro_rules! tuple_from_py_func_args {
988
+ ( $( $T: ident) ,+) => {
989
+ impl <$( $T) ,+> FromPyFuncArgs for ( $( $T, ) +)
990
+ where
991
+ $( $T: FromPyFuncArgs ) ,+
992
+ {
993
+ fn required_params( ctx: & PyContext ) -> Vec <Parameter > {
994
+ vec![ $( $T:: required_params( ctx) , ) +] . into_iter( ) . flatten( ) . collect( )
995
+ }
996
+
997
+ fn from_py_func_args( args: & mut PyFuncArgs ) -> PyResult <Self > {
998
+ Ok ( ( $( $T:: from_py_func_args( args) ?, ) +) )
999
+ }
1000
+ }
1001
+ } ;
1002
+ }
1003
+
1004
+ tuple_from_py_func_args ! ( A ) ;
1005
+ tuple_from_py_func_args ! ( A , B ) ;
1006
+ tuple_from_py_func_args ! ( A , B , C ) ;
1007
+ tuple_from_py_func_args ! ( A , B , C , D ) ;
1008
+ tuple_from_py_func_args ! ( A , B , C , D , E ) ;
1009
+
1010
+ impl < T > FromPyFuncArgs for T
1011
+ where
1012
+ T : FromPyObject ,
1013
+ {
1014
+ fn required_params ( ctx : & PyContext ) -> Vec < Parameter > {
1015
+ vec ! [ Parameter {
1016
+ kind: PositionalOnly ,
1017
+ typ: T :: typ( ctx) ,
1018
+ } ]
1019
+ }
1020
+
1021
+ fn from_py_func_args ( args : & mut PyFuncArgs ) -> PyResult < Self > {
1022
+ Self :: from_pyobject ( args. shift ( ) )
1023
+ }
1024
+ }
1025
+
1026
+ pub type PyNativeFunc = Box < dyn Fn ( & mut VirtualMachine , PyFuncArgs ) -> PyResult > ;
1027
+
1028
+ pub trait PyNativeFuncFactory < T , R > {
1029
+ fn create ( self , ctx : & PyContext ) -> PyNativeFunc ;
1030
+ }
1031
+
1032
+ impl < F > PyNativeFuncFactory < PyFuncArgs , PyResult > for F
1033
+ where
1034
+ F : Fn ( & mut VirtualMachine , PyFuncArgs ) -> PyResult + ' static ,
1035
+ {
1036
+ fn create ( self , _ctx : & PyContext ) -> PyNativeFunc {
1037
+ Box :: new ( self )
1038
+ }
1039
+ }
1040
+
1041
+ macro_rules! tuple_py_native_func_factory {
1042
+ ( $( $T: ident) ,+) => {
1043
+ impl <F , $( $T, ) + R > PyNativeFuncFactory <( $( $T, ) +) , R > for F
1044
+ where
1045
+ F : Fn ( & mut VirtualMachine , $( $T) ,+) -> R + ' static ,
1046
+ $( $T: FromPyFuncArgs , ) +
1047
+ R : IntoPyObject ,
1048
+ {
1049
+ fn create( self , ctx: & PyContext ) -> PyNativeFunc {
1050
+ let parameters = vec![ $( $T:: required_params( ctx) ) ,+]
1051
+ . into_iter( )
1052
+ . flatten( )
1053
+ . collect( ) ;
1054
+ let signature = Signature :: new( parameters) ;
1055
+
1056
+ Box :: new( move |vm, mut args| {
1057
+ signature. check( vm, & mut args) ?;
1058
+
1059
+ ( self ) ( vm, $( $T:: from_py_func_args( & mut args) ?, ) +)
1060
+ . into_pyobject( & vm. ctx)
1061
+ } )
1062
+ }
1063
+ }
1064
+ } ;
1065
+ }
1066
+
1067
+ tuple_py_native_func_factory ! ( A ) ;
1068
+ tuple_py_native_func_factory ! ( A , B ) ;
1069
+ tuple_py_native_func_factory ! ( A , B , C ) ;
1070
+ tuple_py_native_func_factory ! ( A , B , C , D ) ;
1071
+ tuple_py_native_func_factory ! ( A , B , C , D , E ) ;
1072
+
1073
+ #[ derive( Debug ) ]
1074
+ pub struct Signature {
1075
+ positional_params : Vec < Parameter > ,
1076
+ keyword_params : HashMap < String , Parameter > ,
1077
+ }
1078
+
1079
+ impl Signature {
1080
+ fn new ( params : Vec < Parameter > ) -> Self {
1081
+ let mut positional_params = Vec :: new ( ) ;
1082
+ let mut keyword_params = HashMap :: new ( ) ;
1083
+ for param in params {
1084
+ match param. kind {
1085
+ PositionalOnly => {
1086
+ positional_params. push ( param) ;
1087
+ }
1088
+ KeywordOnly { ref name } => {
1089
+ keyword_params. insert ( name. clone ( ) , param) ;
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ Self {
1095
+ positional_params,
1096
+ keyword_params,
1097
+ }
1098
+ }
1099
+
1100
+ fn arg_type ( & self , pos : usize ) -> Option < & PyObjectRef > {
1101
+ self . positional_params [ pos] . typ . as_ref ( )
1102
+ }
1103
+
1104
+ #[ allow( unused) ]
1105
+ fn kwarg_type ( & self , name : & str ) -> Option < & PyObjectRef > {
1106
+ self . keyword_params [ name] . typ . as_ref ( )
1107
+ }
1108
+
1109
+ fn check ( & self , vm : & mut VirtualMachine , args : & PyFuncArgs ) -> PyResult < ( ) > {
1110
+ // TODO: check arity
1111
+
1112
+ for ( pos, arg) in args. args . iter ( ) . enumerate ( ) {
1113
+ if let Some ( expected_type) = self . arg_type ( pos) {
1114
+ if !objtype:: isinstance ( arg, expected_type) {
1115
+ let arg_typ = arg. typ ( ) ;
1116
+ let expected_type_name = vm. to_pystr ( & expected_type) ?;
1117
+ let actual_type = vm. to_pystr ( & arg_typ) ?;
1118
+ return Err ( vm. new_type_error ( format ! (
1119
+ "argument of type {} is required for parameter {} (got: {})" ,
1120
+ expected_type_name,
1121
+ pos + 1 ,
1122
+ actual_type
1123
+ ) ) ) ;
1124
+ }
1125
+ }
1126
+ }
1127
+
1128
+ Ok ( ( ) )
1129
+ }
1130
+ }
1131
+
1132
+ #[ derive( Debug ) ]
1133
+ pub struct Parameter {
1134
+ typ : Option < PyObjectRef > ,
1135
+ kind : ParameterKind ,
1136
+ }
1137
+
1138
+ #[ derive( Debug ) ]
1139
+ pub enum ParameterKind {
1140
+ PositionalOnly ,
1141
+ KeywordOnly { name : String } ,
1142
+ }
1143
+
1144
+ use self :: ParameterKind :: * ;
1145
+
949
1146
/// Rather than determining the type of a python object, this enum is more
950
1147
/// a holder for the rust payload of a python object. It is more a carrier
951
1148
/// of rust data for a particular python object. Determine the python type
0 commit comments