Skip to content

Commit cc0f2ef

Browse files
authored
Merge pull request #279 from rudderlabs/nisshar/VariadicFunctions
Adding support for variadic functions.
2 parents fcad870 + dc5ae78 commit cc0f2ef

File tree

6 files changed

+160
-41
lines changed

6 files changed

+160
-41
lines changed

_examples/variadic/test.py

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Copyright 2018 The go-python Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style
3+
# license that can be found in the LICENSE file.
4+
import variadic, go
5+
6+
############### Non Variadic ##############
7+
nonvarResult = variadic.NonVariFunc(1, go.Slice_int([2,3,4]),5)
8+
print("NonVariadic 1+[2+3+4]+5 = %d" % nonvarResult)
9+
10+
############### Variadic Over Int ##############
11+
varResult = variadic.VariFunc(1,2,3,4,5)
12+
print("Variadic 1+2+3+4+5 = %d" % varResult)
13+
14+
############### Variadic Over Struct ##############
15+
varStructResult = variadic.VariStructFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3))
16+
print("Variadic Struct s(1)+s(2)+s(3) = %d" % varStructResult)
17+
18+
############### Variadic Over InterFace ##############
19+
varInterFaceResult = variadic.VariInterFaceFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3))
20+
print("Variadic InterFace i(1)+i(2)+i(3) = %d" % varInterFaceResult)
21+
22+
############### Final ##############
23+
if isinstance(varResult, int):
24+
print("Type OK")
25+
else:
26+
print("Type Not OK")

_examples/variadic/variadic.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package variadic
2+
3+
/////////////// Non Variadic //////////////
4+
func NonVariFunc(arg1 int, arg2 []int, arg3 int) int{
5+
total := arg1
6+
for _, num := range arg2 {
7+
total += num
8+
}
9+
total += arg3
10+
11+
return total
12+
}
13+
14+
/////////////// Variadic Over Int //////////////
15+
func VariFunc(vargs ...int) int{
16+
total := 0
17+
for _, num := range vargs {
18+
total += num
19+
}
20+
return total
21+
}
22+
23+
/////////////// Variadic Over Struct //////////////
24+
type IntStrUct struct {
25+
p int
26+
}
27+
28+
func NewIntStrUct(n int) IntStrUct {
29+
return IntStrUct {
30+
p:n,
31+
}
32+
}
33+
34+
func VariStructFunc(vargs ...IntStrUct) int{
35+
total := 0
36+
for _, inst := range vargs {
37+
total += inst.p
38+
}
39+
return total
40+
}
41+
42+
/////////////// Variadic Over Interface //////////////
43+
type IntInterFace interface {
44+
Number() int
45+
}
46+
47+
func (is *IntStrUct) Number() int {
48+
return is.p
49+
}
50+
51+
func VariInterFaceFunc(vargs ...IntInterFace) int{
52+
total := 0
53+
for _, inst := range vargs {
54+
total += inst.Number()
55+
}
56+
return total
57+
}

bind/gen_func.go

+41-20
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
8787
return false
8888
}
8989
anm := pySafeArg(arg.Name(), i)
90+
9091
if ifchandle && arg.sym.goname == "interface{}" {
9192
goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, CGoHandle))
9293
pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", PyHandle, anm))
@@ -98,7 +99,10 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
9899
pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", sarg.cpyname, anm))
99100
}
100101
}
101-
wpArgs = append(wpArgs, anm)
102+
103+
if i!=nargs-1 || !fsym.isVariadic {
104+
wpArgs = append(wpArgs, anm)
105+
}
102106
}
103107

104108
// 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 {
108112
wpArgs = append(wpArgs, "goRun=False")
109113
}
110114

115+
// To support variadic args, we add *args at the end.
116+
if fsym.isVariadic {
117+
wpArgs = append(wpArgs, "*args")
118+
}
119+
111120
// When building the pybindgen builder code, we start with
112121
// a function that adds function calls with exception checking.
113122
// But given specific return types, we may want to add more
@@ -276,25 +285,6 @@ if __err != nil {
276285
g.gofile.Printf("var __err error\n")
277286
}
278287

279-
// pywrap output
280-
mnm := fsym.ID()
281-
if isMethod {
282-
mnm = sym.id + "_" + fsym.GoName()
283-
}
284-
rvHasHandle := false
285-
if nres > 0 {
286-
ret := res[0]
287-
if !rvIsErr && ret.sym.hasHandle() {
288-
rvHasHandle = true
289-
cvnm := ret.sym.pyPkgId(g.pkg.pkg)
290-
g.pywrap.Printf("return %s(handle=_%s.%s(", cvnm, pkgname, mnm)
291-
} else {
292-
g.pywrap.Printf("return _%s.%s(", pkgname, mnm)
293-
}
294-
} else {
295-
g.pywrap.Printf("_%s.%s(", pkgname, mnm)
296-
}
297-
298288
callArgs := []string{}
299289
wrapArgs := []string{}
300290
if isMethod {
@@ -313,6 +303,9 @@ if __err != nil {
313303
default:
314304
na = anm
315305
}
306+
if i == len(args) - 1 && fsym.isVariadic {
307+
na = na + "..."
308+
}
316309
callArgs = append(callArgs, na)
317310
switch {
318311
case arg.sym.goname == "interface{}":
@@ -326,6 +319,34 @@ if __err != nil {
326319
default:
327320
wrapArgs = append(wrapArgs, anm)
328321
}
322+
323+
// To support variadic args, we add *args at the end.
324+
if fsym.isVariadic && i == len(args)-1 {
325+
packagePrefix := ""
326+
if arg.sym.gopkg.Name() != fsym.pkg.Name() {
327+
packagePrefix = arg.sym.gopkg.Name() + "."
328+
}
329+
g.pywrap.Printf("%s = %s%s(args)\n", anm, packagePrefix, arg.sym.id)
330+
}
331+
}
332+
333+
// pywrap output
334+
mnm := fsym.ID()
335+
if isMethod {
336+
mnm = sym.id + "_" + fsym.GoName()
337+
}
338+
rvHasHandle := false
339+
if nres > 0 {
340+
ret := res[0]
341+
if !rvIsErr && ret.sym.hasHandle() {
342+
rvHasHandle = true
343+
cvnm := ret.sym.pyPkgId(g.pkg.pkg)
344+
g.pywrap.Printf("return %s(handle=_%s.%s(", cvnm, pkgname, mnm)
345+
} else {
346+
g.pywrap.Printf("return _%s.%s(", pkgname, mnm)
347+
}
348+
} else {
349+
g.pywrap.Printf("_%s.%s(", pkgname, mnm)
329350
}
330351

331352
hasRetCvt := false

bind/symbols.go

-5
Original file line numberDiff line numberDiff line change
@@ -153,11 +153,6 @@ func isPyCompatField(f *types.Var) (*symbol, error) {
153153
func isPyCompatFunc(sig *types.Signature) (ret types.Type, haserr, hasfun bool, err error) {
154154
res := sig.Results()
155155

156-
if sig.Variadic() {
157-
err = fmt.Errorf("gopy: not yet supporting variadic functions: %s", sig.String())
158-
return
159-
}
160-
161156
switch res.Len() {
162157
case 2:
163158
if !isErrorType(res.At(1).Type()) {

bind/types.go

+18-16
Original file line numberDiff line numberDiff line change
@@ -367,12 +367,13 @@ type Func struct {
367367
obj types.Object
368368
name string
369369

370-
id string
371-
doc string
372-
ret types.Type // return type, if any
373-
err bool // true if original go func has comma-error
374-
ctor bool // true if this is a newXXX function
375-
hasfun bool // true if this function has a function argument
370+
id string
371+
doc string
372+
ret types.Type // return type, if any
373+
err bool // true if original go func has comma-error
374+
ctor bool // true if this is a newXXX function
375+
hasfun bool // true if this function has a function argument
376+
isVariadic bool // True, if this is a variadic function.
376377
}
377378

378379
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
392393
}
393394

394395
return &Func{
395-
obj: obj,
396-
pkg: p,
397-
sig: sv,
398-
typ: obj.Type(),
399-
name: obj.Name(),
400-
id: id,
401-
doc: p.getDoc(parent, obj),
402-
ret: ret,
403-
err: haserr,
404-
hasfun: hasfun,
396+
obj: obj,
397+
pkg: p,
398+
sig: sv,
399+
typ: obj.Type(),
400+
name: obj.Name(),
401+
id: id,
402+
doc: p.getDoc(parent, obj),
403+
ret: ret,
404+
err: haserr,
405+
hasfun: hasfun,
406+
isVariadic: sig.Variadic(),
405407
}, nil
406408

407409
// TODO: could optimize by generating code once for each type of callback

main_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ var (
4848
"_examples/gopygc": []string{"py2", "py3"},
4949
"_examples/cstrings": []string{"py2", "py3"},
5050
"_examples/pkgconflict": []string{"py2", "py3"},
51+
"_examples/variadic": []string{"py3"},
5152
}
5253

5354
testEnvironment = os.Environ()
@@ -811,6 +812,23 @@ func TestPkgConflict(t *testing.T) {
811812
// })
812813
// }
813814

815+
func TestBindVariadic(t *testing.T) {
816+
// t.Parallel()
817+
path := "_examples/variadic"
818+
testPkg(t, pkg{
819+
path: path,
820+
lang: features[path],
821+
cmd: "build",
822+
extras: nil,
823+
want: []byte(`NonVariadic 1+[2+3+4]+5 = 15
824+
Variadic 1+2+3+4+5 = 15
825+
Variadic Struct s(1)+s(2)+s(3) = 6
826+
Variadic InterFace i(1)+i(2)+i(3) = 6
827+
Type OK
828+
`),
829+
})
830+
}
831+
814832
// Generate / verify SUPPORT_MATRIX.md from features map.
815833
func TestCheckSupportMatrix(t *testing.T) {
816834
var buf bytes.Buffer

0 commit comments

Comments
 (0)