Skip to content

Commit

Permalink
Merge pull request #279 from rudderlabs/nisshar/VariadicFunctions
Browse files Browse the repository at this point in the history
Adding support for variadic functions.
  • Loading branch information
rcoreilly authored Jun 19, 2022
2 parents fcad870 + dc5ae78 commit cc0f2ef
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 41 deletions.
26 changes: 26 additions & 0 deletions _examples/variadic/test.py
Original file line number Diff line number Diff line change
@@ -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")
57 changes: 57 additions & 0 deletions _examples/variadic/variadic.go
Original file line number Diff line number Diff line change
@@ -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
}
61 changes: 41 additions & 20 deletions bind/gen_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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{}":
Expand All @@ -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
Expand Down
5 changes: 0 additions & 5 deletions bind/symbols.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
34 changes: 18 additions & 16 deletions bind/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand Down
18 changes: 18 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit cc0f2ef

Please sign in to comment.