This is a library that contains python and go utilities for passing data between the two languages. Please note that while I've done my best due diligence I cannot guarentee there are no memory leaks in the current code. I've tested in a bunch of scenarios, but I would recommend being vigilant in any code that uses the library.
To use the python integration download this repo, then extract the folder into your project, and rename it from 📂cgo-python-helpers-main/ to 📂helper/ (or download helper.zip from the releases). From there you can drag it into your project. So for example if your project is called 📂scraper/ then your setup should look like this:
📂scraper/
├─ 📂helper
├─ 📄__init__.puy
└──📄main.py
You can then use the library by doing something like:
from ctypes import cdll
from .helper import prepare_string_array
lib = cdll.LoadLibrary("path/to/library.dll") # Load your Go Library
# Function that takes in string array, and number of items, then prints them in C
lib.print_string_array.argtypes = [POINTER(c_char_p), c_int]
# Prep data using function
data = ["Hello", "World", "!"]
c_array, number_of_items = prepare_string_array(data)
# Use data in Go
lib.print_string_array(c_array, number_of_items)The python lib has the following API functions:
Converting to ctypes
prepare_string(data: str | bytes) -> c_char_p: Takes in a string and returns a C-compatible stringprepare_string_array(data:list[str|bytes]) -> tuple[Array[c_char_p], int]: Takes in a string list, and converts it to a C-compatible arrayprepare_int_array(data:list[int]) -> tuple[Array[c_int], int]: Takes in a int list, and converts it to a C-compatible arrayprepare_float_array(data:list[float]) -> tuple[Array[c_float], int]: Takes in a float list, and converts it to a C-compatible array
Converting from ctypes
string_to_str(pointer: c_char_p) -> str: Takes in a pointer to a C string and returns a Python stringstring_array_result_to_list(pointer:_CStringArrayResult) -> list[str]:int_array_result_to_list(pointer: _CIntArrayResult) -> list[int]:float_array_result_to_list(pointer: _CFloatArrayResult) -> list[float]:
Debugging Functions
return_string(text: str | bytes) -> str: Debugging function that shows you the Go representation of a C string and returns the python string versionreturn_string_array(c_array:CStringArray, number_of_elements:int) ->list[str]: Debugging function that shows you the Go representation of a C array and returns the python list version (does not free)return_int_array(c_array: CIntArray, number_of_elements: int) -> list[int]: Debugging function that shows you the Go representation of a C int array and returns a Python listreturn_float_array(c_array: CFloatArray, number_of_elements: int) -> list[float]: Debugging function that shows you the Go representation of a C float array and returns a Python listprint_string(text: str | bytes): Prints a string's go representation, useful to look for encoding issuesprint_string_array(data:list[str|bytes]): Prints a string array's go representation, useful to look for encoding issuesprint_int_array(data:list[int]): Prints a int array's go representation, useful to look for rounding/conversion issuesprint_float_array(data:list[float]): Prints a float array's go representation, useful to look for rounding/conversion issues
Freeing Functions
free_c_string(ptr: c_char_p): Frees a single C string returned from Go (allocated via C.CString).free_string_array(ptr: CStringArray, count: int): Frees an array of C strings returned from Go.free_int_array(ptr: CIntArray): Frees a C int array returned from Go.free_float_array(ptr: CFloatArray): Frees a C float array returned from Go.free_string_array_result(ptr: _CStringArrayResult): Frees a StringArrayResult (including the array of strings and struct itself).free_int_array_result(ptr: _CIntArrayResult): Frees an IntArrayResult (including the array and the struct itself).free_float_array_result(ptr: _CFloatArrayResult): Frees a FloatArrayResult (including the array and the struct itself).
To run the tests first install pytest:
pip install pytest pytest-covTo run tests install pytest and run:
pytest --ignore=__init__.py --cov-report term-missing --cov=. test_lib.pyThis will run the test suite and let you know any coverage misses. There's ~%80 coverage currently due to some conditions not being possible (or I don't know how to make them happen)
Below are details for hooking up the go side of your code with the helper
In your own go code import the package with:
import (
helpers "github.com/Descent098/cgo-python-helpers/helpers"
)Then run:
go mod tidyHere is an example:
package main
/*
#include <stdio.h>
#include <stdlib.h>
*/
import "C"
import (
"fmt"
helpers "github.com/Descent098/cgo-python-helpers"
)
func main() {
// Sample data
numbers := []int{1, 2, 3, 4, 5}
// Convert Go slice to C-compatible struct
cIntArray := helpers.IntSliceToCArray(numbers)
fmt.Printf("Converted to C: %v elements\n", cIntArray.numberOfElements)
// Convert back to Go slice
goSlice := helpers.CIntArrayToSlice(cIntArray.data, int(cIntArray.numberOfElements))
fmt.Printf("Back to Go: %v\n", goSlice)
// Clean up memory
helpers.FreeIntArray(cIntArray.data)
C.free(unsafe.Pointer(cIntArray)) // or helpers.free_int_array_result(cIntArray) if exported
}The go lib has the following API functions:
Convert C types to go types (internal; Use at entrypoint to Go libraries)
CStringToString(input *C.char) string{}: Convert a string to a c-compatible C-string (glorified alias for C.GoString)CFloatArrayToSlice(cArray *C.float, length int) []float32{}: Converts a C array of floats to a slice of floatsCIntArrayToSlice(cArray *C.int, length int) []int{}: Takes a C integer array and coverts it to an integer sliceCStringArrayToSlice(cArray **C.char, numberOfStrings int) []string{}: Takes in an array of strings, and converts it to a slice of strings
Convert Go types to C types (external; Use to prep data to return to C)
StringToCString(data string) *C.char{}: Convert a string to a c-compatible C-string (glorified alias for C.CString)StringSliceToCArray(data []string) *C.StringArrayResult{}: Return dynamically sized string array as a C-Compatible arrayIntSliceToCArray(data []int) *C.IntArrayResult{}: Return dynamically sized int array as a C-Compatible arrayFloatSliceToCArray(data []float32) *C.FloatArrayResult{}: Return dynamically float sized array as a C-Compatible array
Memory Freeing
FreeCString(data *C.char){}: Free's a C-stringFreeStringArray(inputArray **C.char, count C.int){}: Free's an array of stringsFreeIntArray(ptr *C.int){}: Free's an array of integersFreeFloatArray(ptr *C.float){}: Free's an array of floats
Debugging Functions
return_string(data *C.char) *C.char{}: Used to convert a C-compatible string to a C-compatible string, useful for debugging encoding issuesreturn_string_array(cArray **C.char, numberOfStrings int) *C.StringArrayResult{}: Used to convert a C-compatible string array to wrapper typereturn_int_array(cArray *C.int, numberOfElements C.int) *C.IntArrayResult{}: Used to convert a C-compatible integer array to wrapper typereturn_float_array(cArray *C.float, numberOfElements C.int) *C.FloatArrayResult{}: Used to convert a C-compatible float array to wrapper typeprint_string(ptr *C.char){}: Prints the go representation of a C string, good for debugging encoding issuesprint_string_array(cArray **C.char, numberOfString int){}: Prints the go representation of an array, good for debugging encoding issuesprint_int_array(cArray *C.int, numberOfInts int){}: Prints the go representation of an array, good for debugging rounding/conversion issuesprint_float_array(cArray *C.float, numberOfFloats int){}: Prints the go representation of an array, good for debugging rounding/conversion issues
To run the tests use:
go test