@@ -25,6 +25,10 @@ import (
2525 "unsafe"
2626)
2727
28+ func FuncOf (fn any ) Func {
29+ return CreateFunc ("" , fn , "" )
30+ }
31+
2832func CreateFunc (name string , fn any , doc string ) Func {
2933 m := MainModule ()
3034 return m .AddMethod (name , fn , doc )
@@ -583,7 +587,24 @@ func (m Module) AddMethod(name string, fn any, doc string) Func {
583587 }
584588 }
585589 name = goNameToPythonName (name )
586- doc = name + doc
590+
591+ hasRecv := false
592+ if t .NumIn () > 0 {
593+ firstParam := t .In (0 )
594+ if firstParam .Kind () == reflect .Ptr || firstParam .Kind () == reflect .Interface {
595+ hasRecv = true
596+ }
597+ }
598+
599+ kwargsType := reflect .TypeOf (KwArgs {})
600+ hasKwArgs := false
601+ if t .NumIn () > 0 && t .In (t .NumIn ()- 1 ) == kwargsType {
602+ hasKwArgs = true
603+ }
604+
605+ sig := genSig (fn , hasRecv )
606+ fullDoc := name + sig + "\n --\n \n " + doc
607+ cDoc := C .CString (fullDoc )
587608
588609 maps := getGlobalData ()
589610 meta , ok := maps .typeMetas [m .obj ]
@@ -596,23 +617,25 @@ func (m Module) AddMethod(name string, fn any, doc string) Func {
596617
597618 methodId := uint (len (meta .methods ))
598619
599- methodPtr := C .wrapperMethods [methodId ]
600620 cName := C .CString (name )
601- cDoc := C .CString (doc )
602621
603622 def := (* C .PyMethodDef )(C .malloc (C .size_t (unsafe .Sizeof (C.PyMethodDef {}))))
604623 def .ml_name = cName
605- def .ml_meth = C .PyCFunction (methodPtr )
624+ def .ml_meth = C .PyCFunction (C . wrapperMethods [ methodId ] )
606625 def .ml_flags = C .METH_VARARGS
626+ if hasKwArgs {
627+ def .ml_flags |= C .METH_KEYWORDS
628+ def .ml_meth = C .PyCFunction (C .wrapperMethodsWithKwargs [methodId ])
629+ }
607630 def .ml_doc = cDoc
608631
609632 methodMeta := & slotMeta {
610633 name : name ,
611634 methodName : name ,
612635 fn : fn ,
613636 typ : t ,
614- doc : doc ,
615- hasRecv : false ,
637+ doc : fullDoc ,
638+ hasRecv : hasRecv ,
616639 def : def ,
617640 }
618641 meta .methods [methodId ] = methodMeta
@@ -665,3 +688,124 @@ func FetchError() error {
665688
666689 return fmt .Errorf ("python error: %s" , C .GoString (cstr ))
667690}
691+
692+ func genSig (fn any , hasRecv bool ) string {
693+ t := reflect .TypeOf (fn )
694+ if t .Kind () != reflect .Func {
695+ panic ("genSig: fn must be a function" )
696+ }
697+
698+ var args []string
699+ startIdx := 0
700+ if hasRecv {
701+ startIdx = 1 // skip receiver
702+ }
703+
704+ kwargsType := reflect .TypeOf (KwArgs {})
705+ hasKwArgs := false
706+ lastParamIdx := t .NumIn () - 1
707+ if lastParamIdx >= startIdx && t .In (lastParamIdx ) == kwargsType {
708+ hasKwArgs = true
709+ lastParamIdx -- // don't include KwArgs in regular parameters
710+ }
711+
712+ for i := startIdx ; i <= lastParamIdx ; i ++ {
713+ paramName := fmt .Sprintf ("arg%d" , i - startIdx )
714+ args = append (args , paramName )
715+ }
716+
717+ // add "/" separator only if there are parameters
718+ if len (args ) > 0 {
719+ args = append (args , "/" )
720+ }
721+
722+ // add "**kwargs" if there are keyword arguments
723+ if hasKwArgs {
724+ args = append (args , "**kwargs" )
725+ }
726+
727+ return fmt .Sprintf ("(%s)" , strings .Join (args , ", " ))
728+ }
729+
730+ //export wrapperMethodWithKwargs
731+ func wrapperMethodWithKwargs (self , args , kwargs * C.PyObject , methodId C.int ) * C.PyObject {
732+ key := self
733+ if C .isModule (self ) == 0 {
734+ key = (* C .PyObject )(unsafe .Pointer (self .ob_type ))
735+ }
736+
737+ maps := getGlobalData ()
738+ typeMeta , ok := maps .typeMetas [key ]
739+ check (ok , fmt .Sprintf ("type %v not registered" , FromPy (key )))
740+
741+ methodMeta := typeMeta .methods [uint (methodId )]
742+ methodType := methodMeta .typ
743+ hasReceiver := methodMeta .hasRecv
744+
745+ expectedArgs := methodType .NumIn ()
746+ if hasReceiver {
747+ expectedArgs -- // skip receiver
748+ }
749+ expectedArgs -- // skip KwArgs
750+
751+ argc := C .PyTuple_Size (args )
752+ if int (argc ) != expectedArgs {
753+ SetTypeError (fmt .Errorf ("method %s expects %d arguments, got %d" , methodMeta .name , expectedArgs , argc ))
754+ return nil
755+ }
756+
757+ goArgs := make ([]reflect.Value , methodType .NumIn ())
758+ argIndex := 0
759+
760+ if hasReceiver {
761+ wrapper := (* wrapperType )(unsafe .Pointer (self ))
762+ receiverType := methodType .In (0 )
763+ var recv reflect.Value
764+
765+ if receiverType .Kind () == reflect .Ptr {
766+ recv = reflect .ValueOf (wrapper .goObj )
767+ } else {
768+ recv = reflect .ValueOf (wrapper .goObj ).Elem ()
769+ }
770+
771+ goArgs [0 ] = recv
772+ argIndex = 1
773+ }
774+
775+ for i := 0 ; i < int (argc ); i ++ {
776+ arg := C .PySequence_GetItem (args , C .Py_ssize_t (i ))
777+ argType := methodType .In (i + argIndex )
778+ argPy := FromPy (arg )
779+ goValue := reflect .New (argType ).Elem ()
780+ if ! ToValue (argPy , goValue ) {
781+ SetTypeError (fmt .Errorf ("failed to convert argument %v to %v" , argPy , argType ))
782+ return nil
783+ }
784+ goArgs [i + argIndex ] = goValue
785+ }
786+
787+ kwargsValue := make (KwArgs )
788+ if kwargs != nil {
789+ dict := newDict (kwargs )
790+ dict .Items ()(func (key , value Object ) bool {
791+ kwargsValue [key .String ()] = value
792+ return true
793+ })
794+ }
795+ goArgs [len (goArgs )- 1 ] = reflect .ValueOf (kwargsValue )
796+
797+ results := reflect .ValueOf (methodMeta .fn ).Call (goArgs )
798+
799+ if len (results ) == 0 {
800+ return None ().cpyObj ()
801+ }
802+ if len (results ) == 1 {
803+ return From (results [0 ].Interface ()).cpyObj ()
804+ }
805+
806+ tuple := MakeTupleWithLen (len (results ))
807+ for i := range results {
808+ tuple .Set (i , From (results [i ].Interface ()))
809+ }
810+ return tuple .cpyObj ()
811+ }
0 commit comments