Skip to content

Instantly share code, notes, and snippets.

@neilyoung
Last active January 24, 2024 18:51
Show Gist options
  • Save neilyoung/10d196863fd091780a1fc96dca832a2b to your computer and use it in GitHub Desktop.
Save neilyoung/10d196863fd091780a1fc96dca832a2b to your computer and use it in GitHub Desktop.
Python, GO interaction with callback from GO to Python

Aim

Provide a callback in Python code which expects a JSON string. Forward this to a GO dynamic library and make GO calling the Python callback with a JSON string

Prepare

Python 3 and Go >= 1.18 required. Go module mode (go mod init ....)

Python code (callback.py)

import ctypes

# Define callback type for Python 
CALLBACK_TYPE_JSON_STRING = ctypes.CFUNCTYPE(None, ctypes.c_char_p)

def python_callback(jsonStr):
    print(f"Python callback received: '{jsonStr.decode('utf-8')}'")

# Load Go library
golang_lib = ctypes.CDLL('./callback.so')

# Call a function in GO which immediately calls back
golang_lib.callme_with_json(CALLBACK_TYPE_JSON_STRING(python_callback))

GO code (callback.go)

package main

/*
#include <stdlib.h>
typedef void (*ptr_to_function_expecting_json_string) (char*);

static inline void call_c_func_with_json_str(ptr_to_function_expecting_json_string ptr, char* jsonStr) {
    (ptr)(jsonStr);
}

*/
import "C"
import (
	"fmt"
	"unsafe"
)

//export callme_with_json
func callme_with_json(fn C.ptr_to_function_expecting_json_string) {
	fmt.Println("Calling python callback with JSON string")

	jsonStr := C.CString(`{"the_answer_is": 42}`)

	defer C.free(unsafe.Pointer(jsonStr))
	C.call_c_func_with_json_str(fn, jsonStr)
}

func main() {}

Compile

go build -o callback.so -buildmode=c-shared callback.go

Run

python3 callback.py

The string should be copied in Python code for further processing, since the

Expected output

Calling python callback with JSON string
Python callback received: '{"the_answer_is": 42}'

For further processing of the given JSON string it is most likely not such a bad idea to make an object from the string in the Python callback. Go will release he memory of the CString as soon as the callback is called and the Go function is left.

@neilyoung
Copy link
Author

Note: Doesn't work if the Python callback is called from another goroutine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment