diff --git a/SUPPORT_MATRIX.md b/SUPPORT_MATRIX.md index dc927cb..b7b6a51 100644 --- a/SUPPORT_MATRIX.md +++ b/SUPPORT_MATRIX.md @@ -23,6 +23,7 @@ _examples/lot | yes _examples/maps | yes _examples/named | yes _examples/osfile | yes +_examples/packet | yes _examples/pkgconflict | yes _examples/pointers | yes _examples/pyerrors | yes diff --git a/_examples/packet/packet.go b/_examples/packet/packet.go new file mode 100644 index 0000000..cc75d65 --- /dev/null +++ b/_examples/packet/packet.go @@ -0,0 +1,57 @@ +// Copyright 2015-present, 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. + +// Package packet exercises parameters named after their package, which the +// generated cgo wrappers use to qualify calls (issue #381). +package packet + +import ( + "bytes" + "fmt" +) + +type Packet interface { + Marshal(w *bytes.Buffer) +} + +type Ping struct { + ID int +} + +func NewPing(id int) *Ping { + return &Ping{ID: id} +} + +func (p *Ping) Marshal(w *bytes.Buffer) { + fmt.Fprintf(w, "ping:%d", p.ID) +} + +// GoPacketToBytes is the function from issue #381: an interface-typed +// parameter named after the package. +func GoPacketToBytes(shieldID int, packet Packet) ([]byte, error) { + buf := bytes.NewBuffer([]byte{}) + fmt.Fprintf(buf, "%d/", shieldID) + packet.Marshal(buf) + return buf.Bytes(), nil +} + +// PingID takes a struct-typed parameter named after the package. +func PingID(packet *Ping) int { + return packet.ID +} + +// Double takes a basic-typed parameter named after the package. +func Double(packet int) int { + return 2 * packet +} + +// Add takes a parameter named after the package, on a method. +func (p *Ping) Add(packet int) int { + return p.ID + packet +} + +// Apply takes a callback parameter named after the package. +func Apply(n int, packet func(i int) int) int { + return packet(n) +} diff --git a/_examples/packet/test.py b/_examples/packet/test.py new file mode 100644 index 0000000..aa063b1 --- /dev/null +++ b/_examples/packet/test.py @@ -0,0 +1,15 @@ +# Copyright 2015-present, 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 packet + +p = packet.NewPing(7) +print("GoPacketToBytes(3, p) = %s" % bytes(packet.GoPacketToBytes(3, p)).decode()) +print("GoPacketToBytes(shieldID=4, packet=p) = %s" % bytes(packet.GoPacketToBytes(shieldID=4, packet=p)).decode()) +print("PingID(p) = %d" % packet.PingID(p)) +print("Double(21) = %d" % packet.Double(21)) +print("p.Add(3) = %d" % p.Add(3)) +print("Double(packet=5) = %d" % packet.Double(packet=5)) +print("p.Add(packet=1) = %d" % p.Add(packet=1)) +print("Apply(4, square) = %d" % packet.Apply(4, lambda i: i * i)) diff --git a/bind/gen_func.go b/bind/gen_func.go index db09390..db859dc 100644 --- a/bind/gen_func.go +++ b/bind/gen_func.go @@ -74,17 +74,18 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool { return false } anm := pySafeArg(arg.Name(), i) + goAnm := goSafeArg(arg.Name(), i, fsym.pkg.Name()) - cbGoArg, cbPyArg, isCb := g.shim.callbackParam(g, anm, sarg) + cbGoArg, cbPyArg, isCb := g.shim.callbackParam(g, goAnm, sarg) switch { case isCb: goArgs = append(goArgs, cbGoArg) pyArgs = append(pyArgs, cbPyArg) case ifchandle && arg.sym.goname == "interface{}": - goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, CGoHandle)) + goArgs = append(goArgs, fmt.Sprintf("%s %s", goAnm, CGoHandle)) pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", PyHandle, anm)) default: - goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, sarg.cgoname)) + goArgs = append(goArgs, fmt.Sprintf("%s %s", goAnm, sarg.cgoname)) if sarg.cpyname == "PyObject*" { pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s', transfer_ownership=False)", sarg.cpyname, anm)) } else { @@ -233,10 +234,11 @@ func (g *pyGen) genFuncComplexPlainC(fsym *Func) bool { var goArgs, pyArgs, callArgs, wpArgs []string for i, arg := range args { anm := pySafeArg(arg.Name(), i) + goAnm := goSafeArg(arg.Name(), i, fsym.pkg.Name()) sarg := current.symtype(arg.GoType()) - goArgs = append(goArgs, g.cgoParam(anm, sarg)) + goArgs = append(goArgs, g.cgoParam(goAnm, sarg)) pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", g.cpyName(sarg), anm)) - callArgs = append(callArgs, g.cgoToGo(sarg, anm)) + callArgs = append(callArgs, g.cgoToGo(sarg, goAnm)) wpArgs = append(wpArgs, anm) } @@ -314,7 +316,7 @@ func (g *pyGen) genFuncBody(sym *symbol, fsym *Func) { if fsym.hasfun { for i, arg := range args { if arg.sym.isSignature() { - g.gofile.Print(g.shim.callbackPrologue(pySafeArg(arg.Name(), i))) + g.gofile.Print(g.shim.callbackPrologue(goSafeArg(arg.Name(), i, fsym.pkg.Name()))) } } } @@ -354,15 +356,16 @@ if __err != nil { for i, arg := range args { na := "" anm := pySafeArg(arg.Name(), i) + goAnm := goSafeArg(arg.Name(), i, fsym.pkg.Name()) switch { case ifchandle && arg.sym.goname == "interface{}": - na = fmt.Sprintf(`gopyh.VarFromHandle((gopyh.CGoHandle)(%s), "interface{}")`, anm) + na = fmt.Sprintf(`gopyh.VarFromHandle((gopyh.CGoHandle)(%s), "interface{}")`, goAnm) case arg.sym.isSignature(): - na = g.shim.callbackArg(g, anm, arg.sym) + na = g.shim.callbackArg(g, goAnm, arg.sym) case arg.sym.py2go != "": - na = fmt.Sprintf("%s(%s)%s", arg.sym.py2go, anm, arg.sym.py2goParenEx) + na = fmt.Sprintf("%s(%s)%s", arg.sym.py2go, goAnm, arg.sym.py2goParenEx) default: - na = anm + na = goAnm } if i == len(args)-1 && fsym.isVariadic { na = na + "..." diff --git a/bind/symbols.go b/bind/symbols.go index c97ff0a..4d2e6dc 100644 --- a/bind/symbols.go +++ b/bind/symbols.go @@ -88,6 +88,16 @@ func pySafeArg(anm string, idx int) string { return pySafeName(anm) } +// goSafeArg returns the name of an arg in the generated go code: pySafeArg, +// renamed if it would shadow pkgnm, which that code qualifies calls with. +func goSafeArg(anm string, idx int, pkgnm string) string { + anm = pySafeArg(anm, idx) + if anm == pkgnm { + return "_" + anm + } + return anm +} + // isPyCompatVar checks if var is compatible with python func isPyCompatVar(v *symbol) error { if v == nil { diff --git a/main_test.go b/main_test.go index 9e0339a..2729a15 100644 --- a/main_test.go +++ b/main_test.go @@ -56,6 +56,7 @@ var ( "_examples/gilstring": []string{"py3"}, "_examples/callbacks": []string{"py3"}, "_examples/cgoldflags": []string{"py3"}, + "_examples/packet": []string{"py3"}, } testEnvironment = os.Environ() @@ -1297,6 +1298,26 @@ Type OK }) } +func TestPacket(t *testing.T) { + t.Parallel() + path := "_examples/packet" + testPkg(t, pkg{ + path: path, + lang: features[path], + cmd: "build", + extras: nil, + want: []byte(`GoPacketToBytes(3, p) = 3/ping:7 +GoPacketToBytes(shieldID=4, packet=p) = 4/ping:7 +PingID(p) = 7 +Double(21) = 42 +p.Add(3) = 10 +Double(packet=5) = 10 +p.Add(packet=1) = 8 +Apply(4, square) = 16 +`), + }) +} + // Generate / verify SUPPORT_MATRIX.md from features map. func TestCheckSupportMatrix(t *testing.T) { t.Parallel()