mirror of https://github.com/go-python/gopy.git
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
This commit is contained in:
parent
625ea1c8f9
commit
84915debe0
|
@ -5,7 +5,10 @@
|
|||
// Package pyerrors holds functions returning an error.
|
||||
package pyerrors
|
||||
|
||||
import "errors"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// Div is a function for detecting errors.
|
||||
func Div(i, j int) (int, error) {
|
||||
|
@ -14,3 +17,18 @@ func Div(i, j int) (int, error) {
|
|||
}
|
||||
return i / j, nil
|
||||
}
|
||||
|
||||
type Stringer fmt.Stringer
|
||||
type MyString string
|
||||
|
||||
func (t MyString) String() string { return string(t) }
|
||||
|
||||
// NewMyString converts a string to a custom MyString type.
|
||||
// It is an error to pass an empty string value.
|
||||
func NewMyString(val string) (stringer Stringer, err error) {
|
||||
if val == "" {
|
||||
err = errors.New("Empty string value.")
|
||||
return
|
||||
}
|
||||
return MyString(val), nil
|
||||
}
|
||||
|
|
|
@ -14,7 +14,19 @@ def div(a, b):
|
|||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
div(5,0)
|
||||
|
||||
def new_mystring(s):
|
||||
try:
|
||||
ms = pyerrors.NewMyString(s)
|
||||
print('pyerrors.NewMyString("%s") = "%s"'% (s, ms.String()))
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
div(5,0) # error
|
||||
div(5,2)
|
||||
|
||||
new_mystring("") # error
|
||||
new_mystring("hello")
|
||||
|
||||
print("OK")
|
||||
|
|
23
bind/gen.go
23
bind/gen.go
|
@ -203,9 +203,30 @@ func GoPyMainRun() {
|
|||
# File is generated by gopy. Do not edit.
|
||||
# %[2]s
|
||||
|
||||
from pybindgen import retval, param, Module
|
||||
from pybindgen import retval, param, Function, Module
|
||||
import sys
|
||||
|
||||
class CheckedFunction(Function):
|
||||
def __init__(self, *a, **kw):
|
||||
super(CheckedFunction, self).__init__(*a, **kw)
|
||||
self._failure_expression = kw.get('failure_expression', '')
|
||||
|
||||
def set_failure_expression(self, expr):
|
||||
self._failure_expression = expr
|
||||
|
||||
def generate_call(self):
|
||||
super(CheckedFunction, self).generate_call()
|
||||
check = "PyErr_Occurred()"
|
||||
if self._failure_expression:
|
||||
check = "{} && {}".format(self._failure_expression, check)
|
||||
self.before_call.write_error_check(check)
|
||||
|
||||
def add_checked_function(mod, name, retval, params, failure_expression='', *a, **kw):
|
||||
fn = CheckedFunction(name, retval, params, *a, **kw)
|
||||
fn.set_failure_expression(failure_expression)
|
||||
mod._add_function_obj(fn)
|
||||
return fn
|
||||
|
||||
mod = Module('_%[1]s')
|
||||
mod.add_include('"%[1]s_go.h"')
|
||||
mod.add_function('GoPyInit', None, [])
|
||||
|
|
|
@ -156,14 +156,14 @@ func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool {
|
|||
g.gofile.Printf("\n//export %s\n", mnm)
|
||||
g.gofile.Printf("func %s(", mnm)
|
||||
|
||||
g.pybuild.Printf("mod.add_function('%s', ", mnm)
|
||||
g.pybuild.Printf("add_checked_function(mod, '%s', ", mnm)
|
||||
|
||||
g.pywrap.Printf("def %s(", gname)
|
||||
default:
|
||||
g.gofile.Printf("\n//export %s\n", fsym.ID())
|
||||
g.gofile.Printf("func %s(", fsym.ID())
|
||||
|
||||
g.pybuild.Printf("mod.add_function('%s', ", fsym.ID())
|
||||
g.pybuild.Printf("add_checked_function(mod, '%s', ", fsym.ID())
|
||||
|
||||
g.pywrap.Printf("def %s(", gname)
|
||||
}
|
||||
|
|
12
main_test.go
12
main_test.go
|
@ -181,7 +181,7 @@ Add(int i, int j) int
|
|||
--- hi.LookupQuestion(42)...
|
||||
Life, the Universe and Everything
|
||||
--- hi.LookupQuestion(12)...
|
||||
caught: <built-in function hi_LookupQuestion> returned a result with an error set
|
||||
caught: Wrong answer: 12 != 42
|
||||
--- doc(hi.Person):
|
||||
Person is a simple struct
|
||||
|
||||
|
@ -209,9 +209,9 @@ hi.Person{Name="foo", Age=42}
|
|||
--- p.Name: foo
|
||||
--- p.Work(2)...
|
||||
--- p.Work(24)...
|
||||
caught: <built-in function hi_Person_Work> returned a result with an error set
|
||||
caught: can't work for 24 hours!
|
||||
--- p.Salary(2): 20
|
||||
--- p.Salary(24): caught: <built-in function hi_Person_Salary> returned a result with an error set
|
||||
--- p.Salary(24): caught: can't work for 24 hours!
|
||||
--- Person.__init__
|
||||
caught: argument 2 must be str, not int | err-type: <class 'TypeError'>
|
||||
caught: an integer is required (got type str) | err-type: <class 'TypeError'>
|
||||
|
@ -545,11 +545,13 @@ func TestPyErrors(t *testing.T) {
|
|||
path := "_examples/pyerrors"
|
||||
testPkg(t, pkg{
|
||||
path: path,
|
||||
lang: features[path], // TODO: should print out the error message!
|
||||
lang: features[path],
|
||||
cmd: "build",
|
||||
extras: nil,
|
||||
want: []byte(`<built-in function pyerrors_Div> returned a result with an error set
|
||||
want: []byte(`Divide by zero.
|
||||
pyerrors.Div(5, 2) = 2
|
||||
Empty string value.
|
||||
pyerrors.NewMyString("hello") = "hello"
|
||||
OK
|
||||
`),
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue