diff --git a/_examples/variadic/test.py b/_examples/variadic/test.py new file mode 100644 index 0000000..b53f80f --- /dev/null +++ b/_examples/variadic/test.py @@ -0,0 +1,26 @@ +# 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 + +############### 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) + +############### 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) + +############### 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: + print("Type Not OK") diff --git a/_examples/variadic/variadic.go b/_examples/variadic/variadic.go new file mode 100644 index 0000000..dce6447 --- /dev/null +++ b/_examples/variadic/variadic.go @@ -0,0 +1,57 @@ +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 { + total += num + } + return total +} + +/////////////// Variadic Over Struct ////////////// +type IntStrUct struct { + p int +} + +func NewIntStrUct(n int) IntStrUct { + return IntStrUct { + p:n, + } +} + +func VariStructFunc(vargs ...IntStrUct) int{ + total := 0 + for _, inst := range vargs { + total += inst.p + } + return total +} + +/////////////// 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/bind/gen_func.go b/bind/gen_func.go index d36fab3..991165f 100644 --- a/bind/gen_func.go +++ b/bind/gen_func.go @@ -87,6 +87,7 @@ 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)) @@ -98,7 +99,10 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool { 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,34 @@ 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 { + 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) + } + } + + // 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 e3c29a3..a9249d2 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 574f3d6..1de871e 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 db1061a..12a0193 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,23 @@ 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(`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 +`), + }) +} + // Generate / verify SUPPORT_MATRIX.md from features map. func TestCheckSupportMatrix(t *testing.T) { var buf bytes.Buffer