Skip to content

Commit 84915de

Browse files
committed
bind: WIP add checks for exception being set in wrapped python functions
This commit starts to address the issue of a non-NULL PyObject* being returned when an exception has been set by the wrapped C function. Adds a checking mechanism for PyErr_Occurred. Results in exceptions being propagated to python caller, but may introduce memory leaks for retval not being cleaned up during error. Refs go-python/gopy #254
1 parent 625ea1c commit 84915de

File tree

5 files changed

+63
-10
lines changed

5 files changed

+63
-10
lines changed

_examples/pyerrors/pyerrors.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
// Package pyerrors holds functions returning an error.
66
package pyerrors
77

8-
import "errors"
8+
import (
9+
"errors"
10+
"fmt"
11+
)
912

1013
// Div is a function for detecting errors.
1114
func Div(i, j int) (int, error) {
@@ -14,3 +17,18 @@ func Div(i, j int) (int, error) {
1417
}
1518
return i / j, nil
1619
}
20+
21+
type Stringer fmt.Stringer
22+
type MyString string
23+
24+
func (t MyString) String() string { return string(t) }
25+
26+
// NewMyString converts a string to a custom MyString type.
27+
// It is an error to pass an empty string value.
28+
func NewMyString(val string) (stringer Stringer, err error) {
29+
if val == "" {
30+
err = errors.New("Empty string value.")
31+
return
32+
}
33+
return MyString(val), nil
34+
}

_examples/pyerrors/test.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,19 @@ def div(a, b):
1414
except Exception as e:
1515
print(e)
1616

17-
div(5,0)
17+
18+
def new_mystring(s):
19+
try:
20+
ms = pyerrors.NewMyString(s)
21+
print('pyerrors.NewMyString("%s") = "%s"'% (s, ms.String()))
22+
except Exception as e:
23+
print(e)
24+
25+
26+
div(5,0) # error
1827
div(5,2)
1928

29+
new_mystring("") # error
30+
new_mystring("hello")
31+
2032
print("OK")

bind/gen.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,30 @@ func GoPyMainRun() {
203203
# File is generated by gopy. Do not edit.
204204
# %[2]s
205205
206-
from pybindgen import retval, param, Module
206+
from pybindgen import retval, param, Function, Module
207207
import sys
208208
209+
class CheckedFunction(Function):
210+
def __init__(self, *a, **kw):
211+
super(CheckedFunction, self).__init__(*a, **kw)
212+
self._failure_expression = kw.get('failure_expression', '')
213+
214+
def set_failure_expression(self, expr):
215+
self._failure_expression = expr
216+
217+
def generate_call(self):
218+
super(CheckedFunction, self).generate_call()
219+
check = "PyErr_Occurred()"
220+
if self._failure_expression:
221+
check = "{} && {}".format(self._failure_expression, check)
222+
self.before_call.write_error_check(check)
223+
224+
def add_checked_function(mod, name, retval, params, failure_expression='', *a, **kw):
225+
fn = CheckedFunction(name, retval, params, *a, **kw)
226+
fn.set_failure_expression(failure_expression)
227+
mod._add_function_obj(fn)
228+
return fn
229+
209230
mod = Module('_%[1]s')
210231
mod.add_include('"%[1]s_go.h"')
211232
mod.add_function('GoPyInit', None, [])

bind/gen_func.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,14 +156,14 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
156156
g.gofile.Printf("\n//export %s\n", mnm)
157157
g.gofile.Printf("func %s(", mnm)
158158

159-
g.pybuild.Printf("mod.add_function('%s', ", mnm)
159+
g.pybuild.Printf("add_checked_function(mod, '%s', ", mnm)
160160

161161
g.pywrap.Printf("def %s(", gname)
162162
default:
163163
g.gofile.Printf("\n//export %s\n", fsym.ID())
164164
g.gofile.Printf("func %s(", fsym.ID())
165165

166-
g.pybuild.Printf("mod.add_function('%s', ", fsym.ID())
166+
g.pybuild.Printf("add_checked_function(mod, '%s', ", fsym.ID())
167167

168168
g.pywrap.Printf("def %s(", gname)
169169
}

main_test.go

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ Add(int i, int j) int
181181
--- hi.LookupQuestion(42)...
182182
Life, the Universe and Everything
183183
--- hi.LookupQuestion(12)...
184-
caught: <built-in function hi_LookupQuestion> returned a result with an error set
184+
caught: Wrong answer: 12 != 42
185185
--- doc(hi.Person):
186186
Person is a simple struct
187187
@@ -209,9 +209,9 @@ hi.Person{Name="foo", Age=42}
209209
--- p.Name: foo
210210
--- p.Work(2)...
211211
--- p.Work(24)...
212-
caught: <built-in function hi_Person_Work> returned a result with an error set
212+
caught: can't work for 24 hours!
213213
--- p.Salary(2): 20
214-
--- p.Salary(24): caught: <built-in function hi_Person_Salary> returned a result with an error set
214+
--- p.Salary(24): caught: can't work for 24 hours!
215215
--- Person.__init__
216216
caught: argument 2 must be str, not int | err-type: <class 'TypeError'>
217217
caught: an integer is required (got type str) | err-type: <class 'TypeError'>
@@ -545,11 +545,13 @@ func TestPyErrors(t *testing.T) {
545545
path := "_examples/pyerrors"
546546
testPkg(t, pkg{
547547
path: path,
548-
lang: features[path], // TODO: should print out the error message!
548+
lang: features[path],
549549
cmd: "build",
550550
extras: nil,
551-
want: []byte(`<built-in function pyerrors_Div> returned a result with an error set
551+
want: []byte(`Divide by zero.
552552
pyerrors.Div(5, 2) = 2
553+
Empty string value.
554+
pyerrors.NewMyString("hello") = "hello"
553555
OK
554556
`),
555557
})

0 commit comments

Comments
 (0)