Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions _examples/pyerrors/pyerrors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2017 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 pyerrors holds functions returning an error.
package pyerrors

import "errors"

// Div is a function for detecting errors.
func Div(i, j int) (int, error) {
if j == 0 {
return 0, errors.New("Divide by zero.")
}
return i / j, nil
}
18 changes: 18 additions & 0 deletions _examples/pyerrors/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Copyright 2017 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.

## py2/py3 compat
from __future__ import print_function

import pyerrors

def div(a, b):
try:
r = pyerrors.Div(a, b)
print("pyerrors.Div(%d, %d) = %d"% (a, b, r))
except Exception as e:
print(str(e))

div(5,0)
div(5,2)
6 changes: 5 additions & 1 deletion bind/gencffi.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;

extern GoString _cgopy_GoString(char* p0);
extern char* _cgopy_CString(GoString p0);
extern void _cgopy_FreeCString(char* p0);
extern GoUint8 _cgopy_ErrorIsNil(GoInterface p0);
extern char* _cgopy_ErrorString(GoInterface p0);
extern void cgopy_incref(void* p0);
Expand Down Expand Up @@ -81,7 +82,9 @@ class _cffi_helper(object):
@staticmethod
def cffi_cgopy_cnv_c2py_string(c):
s = _cffi_helper.lib._cgopy_CString(c)
return ffi.string(s)
pystr = ffi.string(s)
_cffi_helper.lib._cgopy_FreeCString(s)
return pystr

@staticmethod
def cffi_cgopy_cnv_c2py_int(c):
Expand All @@ -98,6 +101,7 @@ class _cffi_helper(object):
@staticmethod
def cffi_cgopy_cnv_c2py_uint(c):
return int(c)

# make sure Cgo is loaded and initialized
_cffi_helper.lib.cgo_pkg_%[1]s_init()
`
Expand Down
12 changes: 9 additions & 3 deletions bind/gencffi_cdef.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,25 +32,31 @@ func (g *cffiGen) genCdefType(sym *symbol) {

func (g *cffiGen) genCdefFunc(f Func) {
var params []string
var retParams []string
var cdef_ret string
sig := f.sig
rets := sig.Results()
args := sig.Params()

switch len(rets) {
case 0:
cdef_ret = "void"
case 1:
cdef_ret = rets[0].sym.cgoname
default:
cdef_ret = "cgo_func_" + f.name + "_return"
for i := 0; i < len(rets); i++ {
retParam := rets[i].sym.cgoname + " r" + strconv.Itoa(i)
retParams = append(retParams, retParam)
}
cdef_ret = "cgo_func_" + f.id + "_return"
retParamStrings := strings.Join(retParams, "; ")
g.wrapper.Printf("typedef struct { %[1]s; } %[2]s;\n", retParamStrings, cdef_ret)
}

args := sig.Params()
for i := 0; i < len(args); i++ {
paramVar := args[i].sym.cgoname + " " + "p" + strconv.Itoa(i)
params = append(params, paramVar)
}

paramString := strings.Join(params, ", ")
g.wrapper.Printf("extern %[1]s cgo_func_%[2]s(%[3]s);\n", cdef_ret, f.id, paramString)
}
32 changes: 32 additions & 0 deletions bind/gencffi_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,38 @@ func (g *cffiGen) genFuncBody(f Func) {

g.wrapper.Printf("_cffi_helper.lib.cgo_func_%[1]s(%[2]s)\n", f.id, strings.Join(funcArgs, ", "))

if f.err {
switch nres {
case 1:
g.wrapper.Printf("if not _cffi_helper.lib._cgopy_ErrorIsNil(cret):\n")
g.wrapper.Indent()
g.wrapper.Printf("c_err_str = _cffi_helper.lib._cgopy_ErrorString(cret)\n")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't checked, but, do you know what's the memory ownership model for C-strings being passed by _cgopy_ErrorString ?
and what is its interaction with ffi.string ?

ie: who's supposed to free the C-string ?

Copy link
Collaborator Author

@corona10 corona10 Jun 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sbinet
For about ffi.string. I am using this because of Py3 string handling.
(Next PR would be about handling python3 string.)
It's kind of utility function, which copies the string value from string pointer and join into python string. (Internally, it uses ctypes.)

Copy link
Collaborator Author

@corona10 corona10 Jun 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sbinet
Here is the reference from CFFI documentation.

Any operation that would in C return a pointer or array or struct type gives you a fresh cdata >object. Unlike the “original” one, these fresh cdata objects don’t have ownership: they are merely references to existing memory.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok.

g.wrapper.Printf("py_err_str = ffi.string(c_err_str)\n")
g.wrapper.Printf("_cffi_helper.lib._cgopy_FreeCString(c_err_str)\n")
g.wrapper.Printf("raise RuntimeError(py_err_str)\n")
g.wrapper.Outdent()
g.wrapper.Printf("return\n")
return
case 2:
g.wrapper.Printf("if not _cffi_helper.lib._cgopy_ErrorIsNil(cret.r1):\n")
g.wrapper.Indent()
g.wrapper.Printf("c_err_str = _cffi_helper.lib._cgopy_ErrorString(cret.r1)\n")
g.wrapper.Printf("py_err_str = ffi.string(c_err_str)\n")
g.wrapper.Printf("_cffi_helper.lib._cgopy_FreeCString(c_err_str)\n")
g.wrapper.Printf("raise RuntimeError(py_err_str)\n")
g.wrapper.Outdent()
if res[0].sym.hasConverter() {
g.wrapper.Printf("r0 = _cffi_helper.cffi_%[1]s(cret.r0)\n", res[0].sym.c2py)
g.wrapper.Printf("return r0\n")
} else {
g.wrapper.Printf("return cret.r0\n")
}
return
default:
panic(fmt.Errorf("bind: function/method with more than 2 results not supported! (%s)", f.ID()))
}
}

switch nres {
case 0:
// no-op
Expand Down
5 changes: 5 additions & 0 deletions bind/gengo.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,11 @@ func _cgopy_ErrorString(err error) *C.char {
return C.CString(err.Error())
}

//export _cgopy_FreeCString
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sbinet
I add this function to free the C-string manually.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok.

func _cgopy_FreeCString(cs *C.char) {
C.free(unsafe.Pointer(cs))
}

// --- end cgo helpers ---

// --- begin cref helpers ---
Expand Down
17 changes: 17 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -667,3 +667,20 @@ doc(pkg):
`),
})
}

func TestPyErrors(t *testing.T) {
t.Parallel()
testPkg(t, pkg{
path: "_examples/pyerrors",
want: []byte(`Divide by zero.
pyerrors.Div(5, 2) = 2
`),
})

testPkgWithCFFI(t, pkg{
path: "_examples/pyerrors",
want: []byte(`Divide by zero.
pyerrors.Div(5, 2) = 2
`),
})
}