简体   繁体   English

如何从 go 到 python 返回错误字符串

[英]How to return an error string from go to python

I'm writing a shared object in Go (c-shared) which will be loaded and run from python.我正在 Go(c 共享)中编写一个共享的 object,它将从 python 加载和运行。 Everything is working fine, until the Go code needs to return an error.一切正常,直到 Go 代码需要返回错误。 I am converting the error to string using error.Error() but when trying to return that to python, cgo is hitting:我正在使用 error.Error() 将错误转换为字符串,但是当尝试将其返回到 python 时,cgo 正在命中:

panic: runtime error: cgo result has Go pointer

Which is very odd, since this is a string and not a pointer supposedly.这很奇怪,因为这是一个字符串,而不是一个指针。 I know there are no issues with returning go strings via shared object exported function, as I do that in several other places without any issue.我知道通过共享的 object 导出的 function 返回 go 字符串没有问题,因为我在其他几个地方这样做没有任何问题。

The Go code looks like: Go 代码如下所示:

package main

import "C"

//export MyFunction
func MyFunction() string {
    err := CallSomethingInGo()
    if err != nil {
        return err.Error()
    }
    return ""
}

func main() {}

The go code is compiled to.so using buildmode=c-shared and then In the python code, I have something like this: go 代码使用 buildmode=c-shared 编译为.so,然后在 python 代码中,我有这样的东西:

from ctypes import *

lib = cdll.LoadLibrary("./mygocode.so")

class GoString(Structure):
    _fields_ = [("p", c_char_p),("n", c_longlong)]

theFunction = lib.MyFunction
theFunction.restype = GoString

err = theFunction()

When the last line executes and the golang code returns NO error then everything is fine and it works, But.当最后一行执行并且 golang 代码返回 NO 错误时,一切都很好并且可以正常工作,但是。 if the golang code tries to return an error (eg: CallSomethingInGo fails and returns err) then the python code fails with:如果 golang 代码尝试返回错误(例如:CallSomethingInGo 失败并返回 err),则 python 代码将失败并显示:

panic: runtime error: cgo result has Go pointer

I've tried manually returning strings from go to python and it works fine, but trying to return error.Error() (which should be a string per my understanding) fails.我尝试手动将字符串从 go 返回到 python 并且它工作正常,但尝试返回 error.Error() (根据我的理解应该是一个字符串)失败。 What is the correct way to return the string representation of the error to python?将错误的字符串表示形式返回给 python 的正确方法是什么?

One more piece of info - from golang, I did a printf("%T", err) and I see the type of the error is:还有一条信息-来自golang,我做了一个 printf("%T", err) ,我看到错误的类型是:

*os.PathError *os.PathError

I also did printf("%T", err.Error()) and confirmed the type returned by err.Error() was 'string' so I am still not sure why this isn't working.我也做了 printf("%T", err.Error()) 并确认 err.Error() 返回的类型是 'string' 所以我仍然不确定为什么这不起作用。

Even stranger to me...I tried modifying the go functions as shown below for a test, and this code works fine and returns "test" as a string back to python...对我来说甚至更陌生...我尝试修改 go 函数进行测试,如下所示,此代码工作正常并将“test”作为字符串返回给 python...

//export MyFunction
func MyFunction() string {
    err := CallSomethingInGo()
    if err != nil {
        // test
        x := errors.New("test")
        return x.Error()
    }
    return ""
}

I'm so confused, How can that test work.我很困惑,该测试如何工作。 but not err?Error() ?但不是 err?Error() ?

As I said in a comment, you're just not allowed to do that.正如我在评论中所说,您只是不允许这样做。

The rules for calling Go code from C code are outlined in the Cgo documentation , with this particular issue described in this section , in this way (though I have bolded a few sections in particular): Cgo 文档中概述了从 C 代码调用 Go 代码的规则,本节中描述了这个特殊问题,以这种方式(尽管我特别加粗了一些部分):

Passing pointers传递指针

Go is a garbage collected language, and the garbage collector needs to know the location of every pointer to Go memory. Go 是一种垃圾收集语言,垃圾收集器需要知道每个指向 Go memory 的指针的位置。 Because of this, there are restrictions on passing pointers between Go and C.因此,在 Go 和 C 之间传递指针存在限制。

In this section the term Go pointer means a pointer to memory allocated by Go (such as by using the & operator or calling the predefined new function) and the term C pointer means a pointer to memory allocated by C (such as by a call to C.malloc). In this section the term Go pointer means a pointer to memory allocated by Go (such as by using the & operator or calling the predefined new function) and the term C pointer means a pointer to memory allocated by C (such as by a call to C.malloc)。 Whether a pointer is a Go pointer or a C pointer is a dynamic property determined by how the memory was allocated;指针是 Go 指针还是 C 指针是由 memory 的分配方式决定的动态属性; it has nothing to do with the type of the pointer.它与指针的类型无关。

Note that values of some Go types, other than the type's zero value, always include Go pointers.请注意,某些 Go 类型的值,除了类型的零值外,始终包含 Go 指针。 This is true of string , slice, interface, channel, map, and function types.这适用于字符串、切片、接口、通道、map 和 function 类型。 A pointer type may hold a Go pointer or a C pointer.指针类型可以包含 Go 指针或 C 指针。 Array and struct types may or may not include Go pointers, depending on the element types.数组和结构类型可能包括也可能不包括 Go 指针,具体取决于元素类型。 All the discussion below about Go pointers applies not just to pointer types, but also to other types that include Go pointers.下面关于 Go 指针的所有讨论不仅适用于指针类型,还适用于包括 Go 指针在内的其他类型。

Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers. Go code may pass a Go pointer to C provided the Go memory to which it points does not contain any Go pointers. The C code must preserve this property: it must not store any Go pointers in Go memory, even temporarily. C 代码必须保留此属性:它不能在 Go ZCD691B4957F0918CD8D86 中临时存储任何 Go 指针。 When passing a pointer to a field in a struct, the Go memory in question is the memory occupied by the field, not the entire struct.当传递指向结构中字段的指针时,问题中的 Go memory 是该字段占用的 memory,而不是整个结构。 When passing a pointer to an element in an array or slice, the Go memory in question is the entire array or the entire backing array of the slice.当传递指向数组或切片中元素的指针时,所讨论的 Go memory 是切片的整个数组或整个后备数组。

C code may not keep a copy of a Go pointer after the call returns.调用返回后,C 代码可能不会保留 Go 指针的副本。 This includes the _GoString_ type, which, as noted above, includes a Go pointer;这包括 _GoString_ 类型,如上所述,它包括一个 Go 指针; _GoString_ values may not be retained by C code. C 代码可能不会保留 _GoString_ 值。

A Go function called by C code may not return a Go pointer (which implies that it may not return a string, slice, channel, and so forth). A Go function called by C code may not return a Go pointer (which implies that it may not return a string, slice, channel, and so forth). A Go function called by C code may take C pointers as arguments, and it may store non-pointer or C pointer data through those pointers, but it may not store a Go pointer in memory pointed to by a C pointer. A Go function called by C code may take C pointers as arguments, and it may store non-pointer or C pointer data through those pointers, but it may not store a Go pointer in memory pointed to by a C pointer. A Go function called by C code may take a Go pointer as an argument, but it must preserve the property that the Go memory to which it points does not contain any Go pointers. A Go function called by C code may take a Go pointer as an argument, but it must preserve the property that the Go memory to which it points does not contain any Go pointers.

Go code may not store a Go pointer in C memory. Go 代码可能不会在 C ZCD69B4957F06CD818D7B3D691 中存储 Go 指针C code may store Go pointers in C memory, subject to the rule above: it must stop storing the Go pointer when the C function returns. C code may store Go pointers in C memory, subject to the rule above: it must stop storing the Go pointer when the C function returns.

These rules are checked dynamically at runtime.这些规则在运行时动态检查。 The checking is controlled by the cgocheck setting of the GODEBUG environment variable.检查由 GODEBUG 环境变量的 cgocheck 设置控制。 The default setting is GODEBUG=cgocheck=1, which implements reasonably cheap dynamic checks.默认设置是 GODEBUG=cgocheck=1,它实现了相当便宜的动态检查。 These checks may be disabled entirely using GODEBUG=cgocheck=0.可以使用 GODEBUG=cgocheck=0 完全禁用这些检查。 Complete checking of pointer handling, at some cost in run time, is available via GODEBUG=cgocheck=2.通过 GODEBUG=cgocheck=2 可以在运行时完成对指针处理的检查,但需要付出一定的代价。

It is possible to defeat this enforcement by using the unsafe package, and of course there is nothing stopping the C code from doing anything it likes.可以通过使用不安全的 package 来击败这种强制执行,当然没有什么可以阻止 C 代码做任何它喜欢的事情。 However, programs that break these rules are likely to fail in unexpected and unpredictable ways.但是,违反这些规则的程序很可能会以意想不到和不可预知的方式失败。

This is what you are seeing: you have a program that breaks several rules, and now it fails in unexpected and unpredictable ways.这就是您所看到的:您有一个程序违反了多个规则,现在它以意想不到和不可预知的方式失败。 In particular, your lib.MyFunction is特别是,您的lib.MyFunction

a Go function called by C code由 C 代码调用的 Go function

since Python's cdll handlers count as C code.因为 Python 的cdll处理程序算作 C 代码。 You can return nil , as that's the zero-value, but you are not allowed to return Go strings.您可以返回nil ,因为那是零值,但不允许您返回 Go 字符串。 The fact that the empty-string constant (and other string constants from some other error types) is not caught at runtime is a matter of luck.在运行时未捕获空字符串常量(以及来自其他一些错误类型的其他字符串常量)的事实是运气问题。 1 1


1 Whether this is good luck or bad luck depends on your point of view. 1这是好运还是运,取决于你的观点。 If it failed consistently, perhaps you would have consulted the Cgo documentation earlier.如果它一直失败,也许你会更早地查阅 Cgo 文档。 Instead, it fails unpredictably, but not in your most common case.相反,它会意外失败,但在最常见的情况下不会。 What's happening here is that the string constants were compiled to text (or rodata) sections and therefore are not actually dynamically allocated.这里发生的是字符串常量被编译为文本(或rodata)部分,因此实际上并不是动态分配的。 However, some—not all, but some—errors' string bytes are dynamically allocated.然而,一些——不是全部,而是一些——错误的字符串字节动态分配的。 Some os.PathError s point into GC-able memory, and these are the cases that are caught by the一些os.PathError指向 GC-able memory,这些是被

reasonably cheap dynamic checks相当便宜的动态检查

mentioned in the second-to-last paragraph.在倒数第二段中提到。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM