From e3f8ed64f254f167014de04775336ac69523e9c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9mi=20Calixte?= Date: Thu, 6 Dec 2018 12:15:14 -0500 Subject: [PATCH] Interpreter lifecycle --- .gitignore | 1 + lifecycle.go | 280 ++++++++++++++++++++++++++++++++++++++++++++++ lifecycle_test.go | 114 +++++++++++++++++++ 3 files changed, 395 insertions(+) create mode 100644 lifecycle.go create mode 100644 lifecycle_test.go diff --git a/.gitignore b/.gitignore index f1c181e..6e85044 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out +vendor diff --git a/lifecycle.go b/lifecycle.go new file mode 100644 index 0000000..ff6a0be --- /dev/null +++ b/lifecycle.go @@ -0,0 +1,280 @@ +package python3 + +/* +#include "Python.h" +*/ +import "C" +import ( + "fmt" + "unsafe" +) + +// The argument for Py_SetProgramName, Py_SetPath and Py_SetPythonHome should point to a zero-terminated wide character string in static storage +// whose contents will not change for the duration of the program’s execution +var ( + programName *C.wchar_t + pythonPath *C.wchar_t + pythonHome *C.wchar_t +) + +//Py_Initialize : https://docs.python.org/3/c-api/init.html#c.Py_Initialize +func Py_Initialize() { + C.Py_Initialize() +} + +//Py_InitializeEx : https://docs.python.org/3/c-api/init.html#c.Py_InitializeEx +func Py_InitializeEx(initsigs bool) { + if initsigs { + C.Py_InitializeEx(1) + } else { + C.Py_InitializeEx(0) + } +} + +//Py_IsInitialized : https://docs.python.org/3/c-api/init.html#c.Py_IsInitialized +func Py_IsInitialized() bool { + return C.Py_IsInitialized() != 0 +} + +//Py_FinalizeEx : https://docs.python.org/3/c-api/init.html#c.Py_FinalizeEx +func Py_FinalizeEx() int { + return int(C.Py_FinalizeEx()) +} + +//Py_Finalize : https://docs.python.org/3/c-api/init.html#c.Py_Finalize +func Py_Finalize() { + C.Py_Finalize() +} + +//Py_SetStandardStreamEncoding : https://docs.python.org/3/c-api/init.html#c.Py_SetStandardStreamEncoding +func Py_SetStandardStreamEncoding(encoding, errors string) int { + cencoding := C.CString(encoding) + defer C.free(unsafe.Pointer(cencoding)) + + cerrors := C.CString(errors) + defer C.free(unsafe.Pointer(cerrors)) + + return int(C.Py_SetStandardStreamEncoding(cencoding, cerrors)) + +} + +//Py_SetProgramName : https://docs.python.org/3/c-api/init.html#c.Py_SetProgramName +func Py_SetProgramName(name string) error { + cname := C.CString(name) + defer C.free(unsafe.Pointer(cname)) + + newProgramName := C.Py_DecodeLocale(cname, nil) + if newProgramName == nil { + return fmt.Errorf("fail to call Py_DecodeLocale on '%s'", name) + } + C.Py_SetProgramName(newProgramName) + + //no operation is performed if nil + C.PyMem_RawFree(unsafe.Pointer(programName)) + programName = newProgramName + + return nil +} + +//Py_GetProgramName : https://docs.python.org/3/c-api/init.html#c.Py_GetProgramName +func Py_GetProgramName() (string, error) { + wcname := C.Py_GetProgramName() + if wcname == nil { + return "", nil + } + cname := C.Py_EncodeLocale(wcname, nil) + if cname == nil { + return "", fmt.Errorf("fail to call Py_EncodeLocale") + } + defer C.PyMem_Free(unsafe.Pointer(cname)) + + return C.GoString(cname), nil +} + +//Py_GetPrefix : https://docs.python.org/3/c-api/init.html#c.Py_GetPrefix +func Py_GetPrefix() (string, error) { + wcname := C.Py_GetPrefix() + if wcname == nil { + return "", nil + } + cname := C.Py_EncodeLocale(wcname, nil) + if cname == nil { + return "", fmt.Errorf("fail to call Py_EncodeLocale") + } + defer C.PyMem_Free(unsafe.Pointer(cname)) + + return C.GoString(cname), nil +} + +//Py_GetExecPrefix : https://docs.python.org/3/c-api/init.html#c.Py_GetExecPrefix +func Py_GetExecPrefix() (string, error) { + wcname := C.Py_GetExecPrefix() + if wcname == nil { + return "", nil + } + cname := C.Py_EncodeLocale(wcname, nil) + if cname == nil { + return "", fmt.Errorf("fail to call Py_EncodeLocale") + } + defer C.PyMem_Free(unsafe.Pointer(cname)) + + return C.GoString(cname), nil +} + +//Py_GetProgramFullPath : https://docs.python.org/3/c-api/init.html#c.Py_GetProgramFullPath +func Py_GetProgramFullPath() (string, error) { + wcname := C.Py_GetProgramFullPath() + if wcname == nil { + return "", nil + } + cname := C.Py_EncodeLocale(wcname, nil) + if cname == nil { + return "", fmt.Errorf("fail to call Py_EncodeLocale") + } + defer C.PyMem_Free(unsafe.Pointer(cname)) + + return C.GoString(cname), nil +} + +//Py_GetPath : https://docs.python.org/3/c-api/init.html#c.Py_GetPath +func Py_GetPath() (string, error) { + wcname := C.Py_GetPath() + if wcname == nil { + return "", nil + } + cname := C.Py_EncodeLocale(wcname, nil) + if cname == nil { + return "", fmt.Errorf("fail to call Py_EncodeLocale") + } + defer C.PyMem_Free(unsafe.Pointer(cname)) + + return C.GoString(cname), nil +} + +//Py_SetPath : https://docs.python.org/3/c-api/init.html#c.Py_SetPath +func Py_SetPath(path string) error { + cpath := C.CString(path) + defer C.free(unsafe.Pointer(cpath)) + + newPath := C.Py_DecodeLocale(cpath, nil) + if newPath == nil { + return fmt.Errorf("fail to call Py_DecodeLocale on '%s'", path) + } + C.Py_SetPath(newPath) + + C.PyMem_RawFree(unsafe.Pointer(pythonPath)) + pythonHome = newPath + + return nil +} + +//Py_GetVersion : https://docs.python.org/3/c-api/init.html#c.Py_GetVersion +func Py_GetVersion() string { + cversion := C.Py_GetVersion() + return C.GoString(cversion) +} + +//Py_GetPlatform : https://docs.python.org/3/c-api/init.html#c.Py_GetPlatform +func Py_GetPlatform() string { + cplatform := C.Py_GetPlatform() + return C.GoString(cplatform) +} + +//Py_GetCopyright : https://docs.python.org/3/c-api/init.html#c.Py_GetCopyright +func Py_GetCopyright() string { + ccopyright := C.Py_GetCopyright() + return C.GoString(ccopyright) +} + +//Py_GetCompiler : https://docs.python.org/3/c-api/init.html#c.Py_GetCompiler +func Py_GetCompiler() string { + ccompiler := C.Py_GetCompiler() + return C.GoString(ccompiler) +} + +//Py_GetBuildInfo : https://docs.python.org/3/c-api/init.html#c.Py_GetBuildInfo +func Py_GetBuildInfo() string { + cbuildInfo := C.Py_GetBuildInfo() + return C.GoString(cbuildInfo) +} + +//PySys_SetArgvEx : https://docs.python.org/3/c-api/init.html#c.PySys_SetArgvEx +func PySys_SetArgvEx(args []string, updatepath bool) 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 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 + } + + if updatepath { + C.PySys_SetArgvEx(argc, (**C.wchar_t)(unsafe.Pointer(&argv[0])), 1) + } else { + C.PySys_SetArgvEx(argc, (**C.wchar_t)(unsafe.Pointer(&argv[0])), 0) + } + + return nil +} + +//PySys_SetArgv : https://docs.python.org/3/c-api/init.html#c.PySys_SetArgv +func PySys_SetArgv(args []string) 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 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 + } + C.PySys_SetArgv(argc, (**C.wchar_t)(unsafe.Pointer(&argv[0]))) + + return nil +} + +//Py_SetPythonHome : https://docs.python.org/3/c-api/init.html#c.Py_SetPythonHome +func Py_SetPythonHome(home string) error { + chome := C.CString(home) + defer C.free(unsafe.Pointer(chome)) + + newHome := C.Py_DecodeLocale(chome, nil) + if newHome == nil { + return fmt.Errorf("fail to call Py_DecodeLocale on '%s'", home) + } + C.Py_SetPythonHome(newHome) + + C.PyMem_RawFree(unsafe.Pointer(pythonHome)) + pythonHome = newHome + + return nil +} + +//Py_GetPythonHome : https://docs.python.org/3/c-api/init.html#c.Py_GetPythonHome +func Py_GetPythonHome() (string, error) { + wchome := C.Py_GetPythonHome() + if wchome == nil { + return "", nil + } + chome := C.Py_EncodeLocale(wchome, nil) + if chome == nil { + return "", fmt.Errorf("fail to call Py_EncodeLocale") + } + defer C.PyMem_Free(unsafe.Pointer(chome)) + + return C.GoString(chome), nil +} diff --git a/lifecycle_test.go b/lifecycle_test.go new file mode 100644 index 0000000..641ba9b --- /dev/null +++ b/lifecycle_test.go @@ -0,0 +1,114 @@ +package python3 + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestInitialization(t *testing.T) { + + Py_Initialize() + assert.True(t, Py_IsInitialized()) + Py_Finalize() + assert.False(t, Py_IsInitialized()) + +} + +func TestInitializationEx(t *testing.T) { + + Py_Initialize() + assert.True(t, Py_IsInitialized()) + assert.Zero(t, Py_FinalizeEx()) + assert.False(t, Py_IsInitialized()) + +} + +func TestProgramName(t *testing.T) { + Py_Finalize() + + defaultName, err := Py_GetProgramName() + defer Py_SetProgramName(defaultName) + + assert.Nil(t, err) + name := "py3é" + Py_SetProgramName(name) + newName, err := Py_GetProgramName() + assert.Nil(t, err) + assert.Equal(t, name, newName) + +} + +func TestPrefix(t *testing.T) { + prefix, err := Py_GetPrefix() + assert.Nil(t, err) + assert.IsType(t, "", prefix) + +} + +func TestExecPrefix(t *testing.T) { + execPrefix, err := Py_GetExecPrefix() + assert.Nil(t, err) + assert.IsType(t, "", execPrefix) + +} + +func TestProgramFullPath(t *testing.T) { + programFullPath, err := Py_GetProgramFullPath() + assert.Nil(t, err) + assert.IsType(t, "", programFullPath) + +} + +func TestPath(t *testing.T) { + Py_Finalize() + + defaultPath, err := Py_GetPath() + defer Py_SetPath(defaultPath) + + assert.Nil(t, err) + name := "påth" + Py_SetPath(name) + newName, err := Py_GetPath() + assert.Nil(t, err) + assert.Equal(t, name, newName) + +} + +func TestVersion(t *testing.T) { + version := Py_GetVersion() + assert.IsType(t, "", version) +} + +func TestPlatform(t *testing.T) { + platform := Py_GetPlatform() + assert.IsType(t, "", platform) +} + +func TestCopyright(t *testing.T) { + copyright := Py_GetCopyright() + assert.IsType(t, "", copyright) +} + +func TestCompiler(t *testing.T) { + compiler := Py_GetCompiler() + assert.IsType(t, "", compiler) +} + +func TestBuildInfo(t *testing.T) { + buildInfo := Py_GetBuildInfo() + assert.IsType(t, "", buildInfo) +} + +func TestPythonHome(t *testing.T) { + name := "høme" + + defaultHome, err := Py_GetPythonHome() + defer Py_SetPythonHome(defaultHome) + + assert.Nil(t, err) + Py_SetPythonHome(name) + newName, err := Py_GetPythonHome() + assert.Nil(t, err) + assert.Equal(t, name, newName) +}