From ece09eda609f2da819abdff70b6f1e97077b346e Mon Sep 17 00:00:00 2001 From: Nishant Sharma Date: Mon, 6 Jun 2022 11:20:23 +0530 Subject: [PATCH 1/4] One test case of variadic functions now passing. More to clear. --- _examples/variadic/test.py | 15 +++++++++ _examples/variadic/variadic.go | 19 +++++++++++ bind/gen_func.go | 59 ++++++++++++++++++++++------------ bind/symbols.go | 5 --- bind/types.go | 34 +++++++++++--------- main_test.go | 16 +++++++++ 6 files changed, 106 insertions(+), 42 deletions(-) create mode 100644 _examples/variadic/test.py create mode 100644 _examples/variadic/variadic.go diff --git a/_examples/variadic/test.py b/_examples/variadic/test.py new file mode 100644 index 00000000..ffbd5a52 --- /dev/null +++ b/_examples/variadic/test.py @@ -0,0 +1,15 @@ +# Copyright 2018 The go-python Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. +import variadic, go + +varResult = variadic.VariFunc(1,2,3,4,5) +print("Variadic 1+2+3+4+5 = %d" % varResult) + +nonvarResult = variadic.NonVariFunc(1, go.Slice_int([2,3,4]),5) +print("NonVariadic 1+[2+3+4]+5 = %d" % nonvarResult) + +if isinstance(varResult, int): + print("Type OK") +else: + print("Type Not OK") diff --git a/_examples/variadic/variadic.go b/_examples/variadic/variadic.go new file mode 100644 index 00000000..72a02be1 --- /dev/null +++ b/_examples/variadic/variadic.go @@ -0,0 +1,19 @@ +package variadic + +func VariFunc(vargs ...int) int{ + total := 0 + for _, num := range vargs { + total += num + } + return total +} + +func NonVariFunc(arg1 int, arg2 []int, arg3 int) int{ + total := arg1 + for _, num := range arg2 { + total += num + } + total += arg3 + + return total +} diff --git a/bind/gen_func.go b/bind/gen_func.go index d36fab31..aabb7b5e 100644 --- a/bind/gen_func.go +++ b/bind/gen_func.go @@ -87,18 +87,22 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool { return false } anm := pySafeArg(arg.Name(), i) + if ifchandle && arg.sym.goname == "interface{}" { goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, CGoHandle)) pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", PyHandle, anm)) } else { goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, sarg.cgoname)) if sarg.cpyname == "PyObject*" { - pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s', transfer_ownership=False)", sarg.cpyname, anm)) + pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s%s', transfer_ownership=False)", sarg.cpyname, anm)) } else { pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", sarg.cpyname, anm)) } } - wpArgs = append(wpArgs, anm) + + if i!=nargs-1 || !fsym.isVariadic { + wpArgs = append(wpArgs, anm) + } } // support for optional arg to run in a separate go routine -- only if no return val @@ -108,6 +112,11 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool { wpArgs = append(wpArgs, "goRun=False") } + // To support variadic args, we add *args at the end. + if fsym.isVariadic { + wpArgs = append(wpArgs, "*args") + } + // When building the pybindgen builder code, we start with // a function that adds function calls with exception checking. // But given specific return types, we may want to add more @@ -276,25 +285,6 @@ if __err != nil { g.gofile.Printf("var __err error\n") } - // pywrap output - mnm := fsym.ID() - if isMethod { - mnm = sym.id + "_" + fsym.GoName() - } - rvHasHandle := false - if nres > 0 { - ret := res[0] - if !rvIsErr && ret.sym.hasHandle() { - rvHasHandle = true - cvnm := ret.sym.pyPkgId(g.pkg.pkg) - g.pywrap.Printf("return %s(handle=_%s.%s(", cvnm, pkgname, mnm) - } else { - g.pywrap.Printf("return _%s.%s(", pkgname, mnm) - } - } else { - g.pywrap.Printf("_%s.%s(", pkgname, mnm) - } - callArgs := []string{} wrapArgs := []string{} if isMethod { @@ -313,6 +303,9 @@ if __err != nil { default: na = anm } + if i == len(args) - 1 && fsym.isVariadic { + na = na + "..." + } callArgs = append(callArgs, na) switch { case arg.sym.goname == "interface{}": @@ -326,6 +319,30 @@ if __err != nil { default: wrapArgs = append(wrapArgs, anm) } + + // To support variadic args, we add *args at the end. + if fsym.isVariadic && i == len(args)-1 { + g.pywrap.Printf("%s = go.Slice_int(args)\n", anm) + } + } + + // pywrap output + mnm := fsym.ID() + if isMethod { + mnm = sym.id + "_" + fsym.GoName() + } + rvHasHandle := false + if nres > 0 { + ret := res[0] + if !rvIsErr && ret.sym.hasHandle() { + rvHasHandle = true + cvnm := ret.sym.pyPkgId(g.pkg.pkg) + g.pywrap.Printf("return %s(handle=_%s.%s(", cvnm, pkgname, mnm) + } else { + g.pywrap.Printf("return _%s.%s(", pkgname, mnm) + } + } else { + g.pywrap.Printf("_%s.%s(", pkgname, mnm) } hasRetCvt := false diff --git a/bind/symbols.go b/bind/symbols.go index e3c29a34..a9249d26 100644 --- a/bind/symbols.go +++ b/bind/symbols.go @@ -153,11 +153,6 @@ func isPyCompatField(f *types.Var) (*symbol, error) { func isPyCompatFunc(sig *types.Signature) (ret types.Type, haserr, hasfun bool, err error) { res := sig.Results() - if sig.Variadic() { - err = fmt.Errorf("gopy: not yet supporting variadic functions: %s", sig.String()) - return - } - switch res.Len() { case 2: if !isErrorType(res.At(1).Type()) { diff --git a/bind/types.go b/bind/types.go index 574f3d67..1de871e3 100644 --- a/bind/types.go +++ b/bind/types.go @@ -367,12 +367,13 @@ type Func struct { obj types.Object name string - id string - doc string - ret types.Type // return type, if any - err bool // true if original go func has comma-error - ctor bool // true if this is a newXXX function - hasfun bool // true if this function has a function argument + id string + doc string + ret types.Type // return type, if any + err bool // true if original go func has comma-error + ctor bool // true if this is a newXXX function + hasfun bool // true if this function has a function argument + isVariadic bool // True, if this is a variadic function. } func newFuncFrom(p *Package, parent string, obj types.Object, sig *types.Signature) (*Func, error) { @@ -392,16 +393,17 @@ func newFuncFrom(p *Package, parent string, obj types.Object, sig *types.Signatu } return &Func{ - obj: obj, - pkg: p, - sig: sv, - typ: obj.Type(), - name: obj.Name(), - id: id, - doc: p.getDoc(parent, obj), - ret: ret, - err: haserr, - hasfun: hasfun, + obj: obj, + pkg: p, + sig: sv, + typ: obj.Type(), + name: obj.Name(), + id: id, + doc: p.getDoc(parent, obj), + ret: ret, + err: haserr, + hasfun: hasfun, + isVariadic: sig.Variadic(), }, nil // TODO: could optimize by generating code once for each type of callback diff --git a/main_test.go b/main_test.go index db1061ae..b88c02c0 100644 --- a/main_test.go +++ b/main_test.go @@ -48,6 +48,7 @@ var ( "_examples/gopygc": []string{"py2", "py3"}, "_examples/cstrings": []string{"py2", "py3"}, "_examples/pkgconflict": []string{"py2", "py3"}, + "_examples/variadic": []string{"py3"}, } testEnvironment = os.Environ() @@ -811,6 +812,21 @@ func TestPkgConflict(t *testing.T) { // }) // } +func TestBindVariadic(t *testing.T) { + // t.Parallel() + path := "_examples/variadic" + testPkg(t, pkg{ + path: path, + lang: features[path], + cmd: "build", + extras: nil, + want: []byte(`Variadic 1+2+3+4+5 = 15 +NonVariadic 1+[2+3+4]+5 = 15 +Type OK +`), + }) +} + // Generate / verify SUPPORT_MATRIX.md from features map. func TestCheckSupportMatrix(t *testing.T) { var buf bytes.Buffer From 04688ca746d421f076c0ce32dcb14bca0535150d Mon Sep 17 00:00:00 2001 From: Nishant Sharma Date: Mon, 6 Jun 2022 19:42:36 +0530 Subject: [PATCH 2/4] Variadic functions with variable number of struct inputs now working. --- _examples/variadic/test.py | 10 ++++++++-- _examples/variadic/variadic.go | 33 ++++++++++++++++++++++++++++----- bind/gen_func.go | 6 +++++- main_test.go | 5 +++-- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/_examples/variadic/test.py b/_examples/variadic/test.py index ffbd5a52..53bc69b2 100644 --- a/_examples/variadic/test.py +++ b/_examples/variadic/test.py @@ -3,11 +3,17 @@ # license that can be found in the LICENSE file. import variadic, go +############### Non Variadic ############## +nonvarResult = variadic.NonVariFunc(1, go.Slice_int([2,3,4]),5) +print("NonVariadic 1+[2+3+4]+5 = %d" % nonvarResult) + +############### Variadic Over Int ############## varResult = variadic.VariFunc(1,2,3,4,5) print("Variadic 1+2+3+4+5 = %d" % varResult) -nonvarResult = variadic.NonVariFunc(1, go.Slice_int([2,3,4]),5) -print("NonVariadic 1+[2+3+4]+5 = %d" % nonvarResult) +############### Variadic Over Struct ############## +varStructResult = variadic.VariStructFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3)) +print("Variadic Struct s(1)+s(2)+s(3) = %d" % varStructResult) if isinstance(varResult, int): print("Type OK") diff --git a/_examples/variadic/variadic.go b/_examples/variadic/variadic.go index 72a02be1..103c21dd 100644 --- a/_examples/variadic/variadic.go +++ b/_examples/variadic/variadic.go @@ -1,5 +1,17 @@ package variadic +/////////////// Non Variadic ////////////// +func NonVariFunc(arg1 int, arg2 []int, arg3 int) int{ + total := arg1 + for _, num := range arg2 { + total += num + } + total += arg3 + + return total +} + +/////////////// Variadic Over Int ////////////// func VariFunc(vargs ...int) int{ total := 0 for _, num := range vargs { @@ -8,12 +20,23 @@ func VariFunc(vargs ...int) int{ return total } -func NonVariFunc(arg1 int, arg2 []int, arg3 int) int{ - total := arg1 - for _, num := range arg2 { - total += num +/////////////// Variadic Over Struct ////////////// +type IntStrUct struct { + p int +} + +func NewIntStrUct(n int) IntStrUct { + return IntStrUct { + p:n, } - total += arg3 +} +func VariStructFunc(vargs ...IntStrUct) int{ + total := 0 + for _, inst := range vargs { + total += inst.p + } return total } + +/////////////// Variadic Over Interface ////////////// diff --git a/bind/gen_func.go b/bind/gen_func.go index aabb7b5e..d5518e17 100644 --- a/bind/gen_func.go +++ b/bind/gen_func.go @@ -322,7 +322,11 @@ if __err != nil { // To support variadic args, we add *args at the end. if fsym.isVariadic && i == len(args)-1 { - g.pywrap.Printf("%s = go.Slice_int(args)\n", anm) + packagePrefix := "" + if arg.sym.gopkg.Name() != fsym.pkg.Name() { + packagePrefix = arg.sym.gopkg.Name() + "." + } + g.pywrap.Printf("%s = %s%s(args)\n", anm, packagePrefix, arg.sym.id) } } diff --git a/main_test.go b/main_test.go index b88c02c0..9006ffc3 100644 --- a/main_test.go +++ b/main_test.go @@ -820,8 +820,9 @@ func TestBindVariadic(t *testing.T) { lang: features[path], cmd: "build", extras: nil, - want: []byte(`Variadic 1+2+3+4+5 = 15 -NonVariadic 1+[2+3+4]+5 = 15 + want: []byte(`NonVariadic 1+[2+3+4]+5 = 15 +Variadic 1+2+3+4+5 = 15 +Variadic Struct s(1)+s(2)+s(3) = 6 Type OK `), }) From 4eeeffb2ca75df401eacdd31455d4c9f75ff4505 Mon Sep 17 00:00:00 2001 From: Nishant Sharma Date: Mon, 6 Jun 2022 19:50:23 +0530 Subject: [PATCH 3/4] Interface test also passing. --- _examples/variadic/test.py | 5 +++++ _examples/variadic/variadic.go | 15 +++++++++++++++ main_test.go | 1 + 3 files changed, 21 insertions(+) diff --git a/_examples/variadic/test.py b/_examples/variadic/test.py index 53bc69b2..b53f80f4 100644 --- a/_examples/variadic/test.py +++ b/_examples/variadic/test.py @@ -15,6 +15,11 @@ varStructResult = variadic.VariStructFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3)) print("Variadic Struct s(1)+s(2)+s(3) = %d" % varStructResult) +############### Variadic Over InterFace ############## +varInterFaceResult = variadic.VariInterFaceFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3)) +print("Variadic InterFace i(1)+i(2)+i(3) = %d" % varInterFaceResult) + +############### Final ############## if isinstance(varResult, int): print("Type OK") else: diff --git a/_examples/variadic/variadic.go b/_examples/variadic/variadic.go index 103c21dd..dce6447d 100644 --- a/_examples/variadic/variadic.go +++ b/_examples/variadic/variadic.go @@ -40,3 +40,18 @@ func VariStructFunc(vargs ...IntStrUct) int{ } /////////////// Variadic Over Interface ////////////// +type IntInterFace interface { + Number() int +} + +func (is *IntStrUct) Number() int { + return is.p +} + +func VariInterFaceFunc(vargs ...IntInterFace) int{ + total := 0 + for _, inst := range vargs { + total += inst.Number() + } + return total +} diff --git a/main_test.go b/main_test.go index 9006ffc3..12a01930 100644 --- a/main_test.go +++ b/main_test.go @@ -823,6 +823,7 @@ func TestBindVariadic(t *testing.T) { want: []byte(`NonVariadic 1+[2+3+4]+5 = 15 Variadic 1+2+3+4+5 = 15 Variadic Struct s(1)+s(2)+s(3) = 6 +Variadic InterFace i(1)+i(2)+i(3) = 6 Type OK `), }) From dc5ae7816a08714cde0bd1623f08fb9a6b3fca23 Mon Sep 17 00:00:00 2001 From: Nishant Sharma Date: Wed, 8 Jun 2022 03:47:33 +0530 Subject: [PATCH 4/4] Extra format character removed. --- bind/gen_func.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bind/gen_func.go b/bind/gen_func.go index d5518e17..991165f9 100644 --- a/bind/gen_func.go +++ b/bind/gen_func.go @@ -94,7 +94,7 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool { } else { goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, sarg.cgoname)) if sarg.cpyname == "PyObject*" { - pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s%s', transfer_ownership=False)", sarg.cpyname, anm)) + pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s', transfer_ownership=False)", sarg.cpyname, anm)) } else { pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", sarg.cpyname, anm)) }