Skip to content

Commit 35472c0

Browse files
authored
gopy,gopyh,bind: allow for deletion of handles from the map in gopyh
- add support for deleting gopyh handles - add support for checking arguments' type Fixes #217.
1 parent d635ab6 commit 35472c0

File tree

11 files changed

+291
-11
lines changed

11 files changed

+291
-11
lines changed

SUPPORT_MATRIX.md

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ _examples/cgo | yes | yes
1010
_examples/consts | yes | yes
1111
_examples/empty | yes | yes
1212
_examples/funcs | yes | yes
13+
_examples/gopygc | yes | yes
1314
_examples/gostrings | yes | yes
1415
_examples/hi | no | yes
1516
_examples/iface | no | yes

_examples/gopygc/gopygc.go

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2020 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+
5+
// package gopygc tests the interaction between the python gc system and gopy
6+
package gopygc
7+
8+
import "bytes"
9+
10+
type StructA struct {
11+
A int
12+
}
13+
14+
type MapA map[string]string
15+
16+
type SliceA []string
17+
18+
func StructValue() StructA {
19+
return StructA{
20+
A: 42,
21+
}
22+
}
23+
24+
func SliceScalarValue() []int {
25+
return []int{1, 2}
26+
}
27+
28+
func SliceStructValue() []StructA {
29+
return []StructA{{1}, {2}}
30+
}
31+
32+
func MapValue() map[int]int {
33+
return map[int]int{1: 2}
34+
}
35+
36+
func MapValueStruct() map[int]StructA {
37+
return map[int]StructA{1: StructA{3}}
38+
}
39+
40+
func ExternalType() *bytes.Buffer {
41+
return &bytes.Buffer{}
42+
}

_examples/gopygc/test.py

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Copyright 2020 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+
5+
# py2/py3 compat
6+
from __future__ import print_function
7+
8+
import gopygc
9+
import _gopygc
10+
11+
print(_gopygc.NumHandles())
12+
13+
14+
# test literals
15+
a = gopygc.StructA()
16+
b = gopygc.SliceA()
17+
c = gopygc.MapA()
18+
print(_gopygc.NumHandles())
19+
del a
20+
del b
21+
del c
22+
23+
print(_gopygc.NumHandles())
24+
a = [gopygc.StructValue(), gopygc.StructValue(), gopygc.StructValue()]
25+
print(_gopygc.NumHandles()) # 3
26+
b = [gopygc.SliceScalarValue(), gopygc.SliceScalarValue()]
27+
print(_gopygc.NumHandles()) # 5
28+
c = gopygc.SliceStructValue()
29+
print(_gopygc.NumHandles()) # 6
30+
d = gopygc.MapValue()
31+
print(_gopygc.NumHandles()) # 7
32+
e = gopygc.MapValueStruct()
33+
print(_gopygc.NumHandles()) # 8
34+
35+
del a
36+
print(_gopygc.NumHandles()) # 5
37+
del b
38+
print(_gopygc.NumHandles()) # 3
39+
del c
40+
print(_gopygc.NumHandles()) # 2
41+
del d
42+
print(_gopygc.NumHandles()) # 1
43+
del e
44+
print(_gopygc.NumHandles()) # 0
45+
46+
e1 = gopygc.ExternalType()
47+
print(_gopygc.NumHandles()) # 1
48+
del e1
49+
print(_gopygc.NumHandles()) # 0
50+
51+
# test reference counting
52+
f = gopygc.SliceStructValue()
53+
print(_gopygc.NumHandles()) # 1
54+
g = gopygc.StructA(handle=f.handle)
55+
print(_gopygc.NumHandles()) # 1
56+
del g
57+
print(_gopygc.NumHandles()) # 1
58+
del f
59+
print(_gopygc.NumHandles()) # 0
60+
61+
62+
print("OK")

_examples/hi/test.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@
189189
pass
190190

191191
try:
192-
hi.Couple(1,2)
192+
hi.Couple(1, 2)
193193
print("*ERROR* no exception raised!")
194194
except Exception as err:
195195
print("caught:", err, "| err-type:",type(err))

bind/gen.go

+22
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,25 @@ func GoPyInit() {
108108
type GoHandle %[4]s
109109
type CGoHandle %[5]s
110110
111+
// DecRef decrements the reference count for the specified handle
112+
// and deletes it it goes to zero.
113+
//export DecRef
114+
func DecRef(handle CGoHandle) {
115+
gopyh.DecRef(gopyh.CGoHandle(handle))
116+
}
117+
118+
// IncRef increments the reference count for the specified handle.
119+
//export IncRef
120+
func IncRef(handle CGoHandle) {
121+
gopyh.IncRef(gopyh.CGoHandle(handle))
122+
}
123+
124+
// NumHandles returns the number of handles currently in use.
125+
//export NumHandles
126+
func NumHandles() int {
127+
return gopyh.NumHandles()
128+
}
129+
111130
// boolGoToPy converts a Go bool to python-compatible C.char
112131
func boolGoToPy(b bool) C.char {
113132
if b {
@@ -190,6 +209,9 @@ import sys
190209
mod = Module('_%[1]s')
191210
mod.add_include('"%[1]s_go.h"')
192211
mod.add_function('GoPyInit', None, [])
212+
mod.add_function('DecRef', None, [param('int64_t', 'handle')])
213+
mod.add_function('IncRef', None, [param('int64_t', 'handle')])
214+
mod.add_function('NumHandles', retval('int'), [])
193215
`
194216

195217
// appended to imports in py wrap preamble as key for adding at end

bind/gen_map.go

+8
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,17 @@ otherwise parameter is a python list that we copy from
9999
g.pywrap.Printf("if len(kwargs) == 1 and 'handle' in kwargs:\n")
100100
g.pywrap.Indent()
101101
g.pywrap.Printf("self.handle = kwargs['handle']\n")
102+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
102103
g.pywrap.Outdent()
103104
g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], %sGoClass):\n", gocl)
104105
g.pywrap.Indent()
105106
g.pywrap.Printf("self.handle = args[0].handle\n")
107+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
106108
g.pywrap.Outdent()
107109
g.pywrap.Printf("else:\n")
108110
g.pywrap.Indent()
109111
g.pywrap.Printf("self.handle = _%s_CTor()\n", qNm)
112+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
110113
g.pywrap.Printf("if len(args) > 0:\n")
111114
g.pywrap.Indent()
112115
g.pywrap.Printf("if not isinstance(args[0], collections.Mapping):\n")
@@ -121,6 +124,11 @@ otherwise parameter is a python list that we copy from
121124
g.pywrap.Outdent()
122125
g.pywrap.Outdent()
123126

127+
g.pywrap.Printf("def __del__(self):\n")
128+
g.pywrap.Indent()
129+
g.pywrap.Printf("_%s.DecRef(self.handle)\n", g.outname)
130+
g.pywrap.Outdent()
131+
124132
if mpob != nil && mpob.prots&ProtoStringer != 0 {
125133
for _, m := range mpob.meths {
126134
if isStringer(m.obj) {

bind/gen_slice.go

+8
Original file line numberDiff line numberDiff line change
@@ -87,15 +87,18 @@ otherwise parameter is a python list that we copy from
8787
g.pywrap.Printf("if len(kwargs) == 1 and 'handle' in kwargs:\n")
8888
g.pywrap.Indent()
8989
g.pywrap.Printf("self.handle = kwargs['handle']\n")
90+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
9091
g.pywrap.Outdent()
9192
g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], %sGoClass):\n", gocl)
9293
g.pywrap.Indent()
9394
g.pywrap.Printf("self.handle = args[0].handle\n")
95+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
9496
g.pywrap.Outdent()
9597
if slc.isSlice() {
9698
g.pywrap.Printf("else:\n")
9799
g.pywrap.Indent()
98100
g.pywrap.Printf("self.handle = _%s_CTor()\n", qNm)
101+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
99102
g.pywrap.Printf("if len(args) > 0:\n")
100103
g.pywrap.Indent()
101104
g.pywrap.Printf("if not isinstance(args[0], collections.Iterable):\n")
@@ -111,6 +114,11 @@ otherwise parameter is a python list that we copy from
111114
}
112115
g.pywrap.Outdent()
113116

117+
g.pywrap.Printf("def __del__(self):\n")
118+
g.pywrap.Indent()
119+
g.pywrap.Printf("_%s.DecRef(self.handle)\n", g.outname)
120+
g.pywrap.Outdent()
121+
114122
if slob != nil && slob.prots&ProtoStringer != 0 {
115123
for _, m := range slob.meths {
116124
if isStringer(m.obj) {

bind/gen_struct.go

+25-4
Original file line numberDiff line numberDiff line change
@@ -53,23 +53,30 @@ in which case a new Go object is constructed first
5353
g.pywrap.Printf("if len(kwargs) == 1 and 'handle' in kwargs:\n")
5454
g.pywrap.Indent()
5555
g.pywrap.Printf("self.handle = kwargs['handle']\n")
56+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
5657
g.pywrap.Outdent()
5758
g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], go.GoClass):\n")
5859
g.pywrap.Indent()
5960
g.pywrap.Printf("self.handle = args[0].handle\n")
61+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
6062
g.pywrap.Outdent()
6163
g.pywrap.Printf("else:\n")
6264
g.pywrap.Indent()
6365
g.pywrap.Printf("self.handle = _%s.%s_CTor()\n", pkgname, s.ID())
66+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
6467

6568
for i := 0; i < numFields; i++ {
6669
f := s.Struct().Field(i)
6770
if _, err := isPyCompatField(f); err != nil {
6871
continue
6972
}
70-
// TODO: this will accept int args for any handles / object fields
71-
// need some kind of additional type-checking logic to prevent that in way
72-
// that also allows valid handles to be used..
73+
// NOTE: this will accept int args for any handles / object fields so
74+
// some kind of additional type-checking logic to prevent that in a way
75+
// that also allows valid handles to be used as required. This is
76+
// achieved in the per-field setters (see below) with checks to ensure
77+
// that a struct field that is a gopy managed object is only
78+
// assigned gopy managed objects. Fields of basic types (e.g int, string)
79+
// etc can be assigned to directly.
7380
g.pywrap.Printf("if %[1]d < len(args):\n", i)
7481
g.pywrap.Indent()
7582
g.pywrap.Printf("self.%s = args[%d]\n", f.Name(), i)
@@ -82,6 +89,11 @@ in which case a new Go object is constructed first
8289
g.pywrap.Outdent()
8390
g.pywrap.Outdent()
8491

92+
g.pywrap.Printf("def __del__(self):\n")
93+
g.pywrap.Indent()
94+
g.pywrap.Printf("_%s.DecRef(self.handle)\n", s.Package().Name())
95+
g.pywrap.Outdent()
96+
8597
if s.prots&ProtoStringer != 0 {
8698
for _, m := range s.meths {
8799
if !isStringer(m.obj) {
@@ -223,7 +235,14 @@ func (g *pyGen) genStructMemberSetter(s *Struct, i int, f types.Object) {
223235
g.pywrap.Outdent()
224236
g.pywrap.Printf("else:\n")
225237
g.pywrap.Indent()
226-
g.pywrap.Printf("_%s.%s(self.handle, value)\n", pkgname, cgoFn)
238+
// See comment in genStructInit about ensuring that gopy managed
239+
// objects are only assigned to from gopy managed objects.
240+
switch f.Type().(type) {
241+
case *types.Basic:
242+
g.pywrap.Printf("_%s.%s(self.handle, value)\n", pkgname, cgoFn)
243+
default:
244+
g.pywrap.Printf("raise TypeError(\"supplied argument type {t} is not a go.GoClass\".format(t=type(value)))\n")
245+
}
227246
g.pywrap.Outdent()
228247
g.pywrap.Outdent()
229248

@@ -279,10 +298,12 @@ handle=A Go-side object is always initialized with an explicit handle=arg
279298
g.pywrap.Printf("if len(kwargs) == 1 and 'handle' in kwargs:\n")
280299
g.pywrap.Indent()
281300
g.pywrap.Printf("self.handle = kwargs['handle']\n")
301+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
282302
g.pywrap.Outdent()
283303
g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], go.GoClass):\n")
284304
g.pywrap.Indent()
285305
g.pywrap.Printf("self.handle = args[0].handle\n")
306+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
286307
g.pywrap.Outdent()
287308
g.pywrap.Printf("else:\n")
288309
g.pywrap.Indent()

bind/gen_type.go

+10
Original file line numberDiff line numberDiff line change
@@ -145,20 +145,30 @@ handle=A Go-side object is always initialized with an explicit handle=arg
145145
g.pywrap.Printf("if len(kwargs) == 1 and 'handle' in kwargs:\n")
146146
g.pywrap.Indent()
147147
g.pywrap.Printf("self.handle = kwargs['handle']\n")
148+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
148149
g.pywrap.Outdent()
149150
g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], GoClass):\n")
150151
g.pywrap.Indent()
151152
g.pywrap.Printf("self.handle = args[0].handle\n")
153+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
152154
g.pywrap.Outdent()
153155
g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], int):\n")
154156
g.pywrap.Indent()
155157
g.pywrap.Printf("self.handle = args[0]\n")
158+
g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.outname)
156159
g.pywrap.Outdent()
157160
g.pywrap.Printf("else:\n")
158161
g.pywrap.Indent()
159162
g.pywrap.Printf("self.handle = 0\n")
160163
g.pywrap.Outdent()
161164
g.pywrap.Outdent()
165+
166+
g.pywrap.Printf("def __del__(self):\n")
167+
g.pywrap.Indent()
168+
g.pywrap.Printf("_%s.DecRef(self.handle)\n", g.outname)
169+
g.pywrap.Outdent()
170+
162171
g.pywrap.Printf("\n")
163172
g.pywrap.Outdent()
173+
164174
}

0 commit comments

Comments
 (0)