diff --git a/README.md b/README.md new file mode 100644 index 0000000..4c454cb --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +go-python3 +========== + +Golang bindings for the C-API of CPython-3. + +This package provides a ``go`` package named "python" under which most of the +``PyXYZ`` functions and macros of the public C-API of CPython have been +exposed. Theoretically, you should be able use https://docs.python.org/3/c-api +and know what to type in your ``go`` program. + +API +--- + +Some functions mix go code and call to Python function. Those functions will +return and `int` and `error` type. The `int` represent the Python result code +and the `error` represent any issue from the Go layer. + +Example: + +`func PyRun_AnyFile(filename string)` open `filename` and then call CPython API +function `int PyRun_AnyFile(FILE *fp, const char *filename)`. + +Therefore its signature is `func PyRun_AnyFile(filename string) (int, error)`, +the `int` represent the error code from the CPython `PyRun_AnyFile` function +and error will be set if we failed to open `filename`. + +If an error is raise before calling th CPython function `int` default to `-1`. diff --git a/high_level_layer.go b/high_level_layer.go new file mode 100644 index 0000000..a27220e --- /dev/null +++ b/high_level_layer.go @@ -0,0 +1,59 @@ +package python3 + +/* +#cgo pkg-config: python3 +#include "Python.h" +*/ +import "C" +import ( + "unsafe" +) + +//Py_Main : https://docs.python.org/3/c-api/veryhigh.html?highlight=pycompilerflags#c.Py_Main +// "error" will be set if we fail to call "Py_DecodeLocale" on every "args". +func Py_Main(args []string) (int, error) { + argc := C.int(len(args)) + argv := make([]*C.wchar_t, argc, argc) + for i, arg := range args { + carg := C.CString(arg) + defer C.free(unsafe.Pointer(carg)) + + warg := C.Py_DecodeLocale(carg, nil) + if warg == nil { + return -1, fmt.Errorf("fail to call Py_DecodeLocale on '%s'", arg) + } + // Py_DecodeLocale requires a call to PyMem_RawFree to free the memory + defer C.PyMem_RawFree(unsafe.Pointer(warg)) + argv[i] = warg + } + + return int(C.Py_Main(argc, (**C.wchar_t)(unsafe.Pointer(&argv[0])))), nil +} + +//PyRun_AnyFile : https://docs.python.org/3/c-api/veryhigh.html?highlight=pycompilerflags#c.PyRun_AnyFile +// "error" will be set if we fail to open "filename". +func PyRun_AnyFile(filename string) (int, error) { + cfilename := C.CString(filename) + defer C.free(unsafe.Pointer(cfilename)) + + mode := C.CString("r") + defer C.free(unsafe.Pointer(mode)) + + cfile, err := C.fopen(cfilename, mode) + if err != nil { + return -1, fmt.Errorf("fail to open '%s': %s", filename, err) + } + defer C.fclose(cfile) + + // C.PyRun_AnyFile is a macro, using C.PyRun_AnyFileFlags instead + return int(C.PyRun_AnyFileFlags(cfile, cfilename, nil)), nil +} + +//PyRun_SimpleString : https://docs.python.org/3/c-api/veryhigh.html?highlight=pycompilerflags#c.PyRun_SimpleString +func PyRun_SimpleString(command string) int { + ccommand := C.CString(command) + defer C.free(unsafe.Pointer(ccommand)) + + // C.PyRun_SimpleString is a macro, using C.PyRun_SimpleStringFlags instead + return int(C.PyRun_SimpleStringFlags(ccommand, nil)) +}