Skip to content
This repository has been archived by the owner on Jul 24, 2023. It is now read-only.

Reorganize package to support both py2 and py3 runtimes #66

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
7 changes: 0 additions & 7 deletions go-python.c → .legacy/go-python.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,10 +450,3 @@ _gopy_PyImport_ImportModuleEx(char *name, PyObject *globals, PyObject *locals, P
//void _gopy_PySys_WriteStdout(const char *data) {
// PySys_WriteStdout("%s", data)
//}

/* --- veryhigh --- */

int
_gopy_PyRun_SimpleString(const char *command) {
return PyRun_SimpleString(command);
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
471 changes: 471 additions & 0 deletions .legacy/object.go

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion otherobjects.go → .legacy/otherobjects.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,7 @@ func PySeqIter_Check(op *PyObject) bool {
//
// Return the next value from the iteration o. The object must be an iterator (it is up to the caller to check this). If there are no remaining values, returns NULL with no exception set. If an error occurs while retrieving the item, returns NULL and passes along the exception.
func PyIter_Next(op *PyObject) *PyObject {
return togo(C.PyIter_Next(topy(op)))
return togo(C.PyIter_Next(topy(op)))
}

// PyObject* PySeqIter_New(PyObject *seq)
Expand Down
104 changes: 104 additions & 0 deletions .legacy/python.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
// simplistic wrapper around the python C-API
package python

//#include "go-python.h"
import "C"

import (
"fmt"
)

// PyGILState is the Go alias for the PyGILState_STATE enum
type PyGILState C.PyGILState_STATE

// PyThreadState layer
type PyThreadState struct {
ptr *C.PyThreadState
}

// Initialize initializes the python interpreter and its GIL
func Initialize() error {
// make sure the python interpreter has been initialized
if C.Py_IsInitialized() == 0 {
C.Py_Initialize()
}
if C.Py_IsInitialized() == 0 {
return fmt.Errorf("python: could not initialize the python interpreter")
}

// make sure the GIL is correctly initialized
if C.PyEval_ThreadsInitialized() == 0 {
C.PyEval_InitThreads()
}
if C.PyEval_ThreadsInitialized() == 0 {
return fmt.Errorf("python: could not initialize the GIL")
}

return nil
}

// Finalize shutdowns the python interpreter
func Finalize() error {
C.Py_Finalize()
return nil
}

// PyThreadState* PyEval_SaveThread()
// Release the global interpreter lock (if it has been created and thread
// support is enabled) and reset the thread state to NULL, returning the
// previous thread state (which is not NULL). If the lock has been created,
// the current thread must have acquired it. (This function is available even
// when thread support is disabled at compile time.)
func PyEval_SaveThread() *PyThreadState {
state := C.PyEval_SaveThread()
return &PyThreadState{ptr: state}
}

// void PyEval_RestoreThread(PyThreadState *tstate)
// Acquire the global interpreter lock (if it has been created and thread
// support is enabled) and set the thread state to tstate, which must not be
// NULL. If the lock has been created, the current thread must not have
// acquired it, otherwise deadlock ensues. (This function is available even
// when thread support is disabled at compile time.)
func PyEval_RestoreThread(state *PyThreadState) {
C.PyEval_RestoreThread(state.ptr)
}

// Ensure that the current thread is ready to call the Python C API regardless
// of the current state of Python, or of the global interpreter lock. This may
// be called as many times as desired by a thread as long as each call is
// matched with a call to PyGILState_Release(). In general, other thread-related
// APIs may be used between PyGILState_Ensure() and PyGILState_Release() calls
// as long as the thread state is restored to its previous state before the
// Release(). For example, normal usage of the Py_BEGIN_ALLOW_THREADS and
// Py_END_ALLOW_THREADS macros is acceptable.
//
// The return value is an opaque “handle” to the thread state when
// PyGILState_Ensure() was called, and must be passed to PyGILState_Release()
// to ensure Python is left in the same state. Even though recursive calls are
// allowed, these handles cannot be shared - each unique call to
// PyGILState_Ensure() must save the handle for its call to PyGILState_Release().
//
// When the function returns, the current thread will hold the GIL and be able
// to call arbitrary Python code. Failure is a fatal error.
//
// New in version 2.3.
func PyGILState_Ensure() PyGILState {
return PyGILState(C.PyGILState_Ensure())
}

// void PyGILState_Release(PyGILState_STATE)
// Release any resources previously acquired. After this call, Python’s state
// will be the same as it was prior to the corresponding PyGILState_Ensure()
// call (but generally this state will be unknown to the caller, hence the use
// of the GILState API).
//
// Every call to PyGILState_Ensure() must be matched by a call to
// PyGILState_Release() on the same thread.
//
// New in version 2.3.
func PyGILState_Release(state PyGILState) {
C.PyGILState_Release(C.PyGILState_STATE(state))
}

// EOF
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
54 changes: 0 additions & 54 deletions utilities.go → .legacy/utilities.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import "C"

import (
"errors"
"fmt"
"unsafe"
)

Expand Down Expand Up @@ -47,33 +46,6 @@ func PyOS_setsig(i int, h C.PyOS_sighandler_t) C.PyOS_sighandler_t {

///// system functions /////

// PyObject *PySys_GetObject(char *name)
// Return value: Borrowed reference.
// Return the object name from the sys module or NULL if it does not exist, without setting an exception.
func PySys_GetObject(name string) *PyObject {
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))

return togo(C.PySys_GetObject(c_name))
}

// FILE *PySys_GetFile(char *name, FILE *def)
// Return the FILE* associated with the object name in the sys module, or def if name is not in the module or is not associated with a FILE*.
func PySys_GetFile(name string, def *C.FILE) *C.FILE {
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))
//FIXME use go os.File ?
return C.PySys_GetFile(c_name, def)
}

// int PySys_SetObject(char *name, PyObject *v)
// Set name in the sys module to v unless v is NULL, in which case name is deleted from the sys module. Returns 0 on success, -1 on error.
func PySys_SetObject(name string, v *PyObject) error {
c_name := C.CString(name)
defer C.free(unsafe.Pointer(c_name))
return int2err(C.PySys_SetObject(c_name, topy(v)))
}

// void PySys_ResetWarnOptions()
// Reset sys.warnoptions to an empty list.
func PySys_ResetWarnOptions() {
Expand All @@ -96,32 +68,6 @@ func PySys_SetPath(path string) {
C.PySys_SetPath(c_path)
}

// void PySys_WriteStdout(const char *format, ...)
// Write the output string described by format to sys.stdout. No exceptions are raised, even if truncation occurs (see below).
//
// format should limit the total size of the formatted output string to 1000 bytes or less – after 1000 bytes, the output string is truncated. In particular, this means that no unrestricted “%s” formats should occur; these should be limited using “%.<N>s” where <N> is a decimal number calculated so that <N> plus the maximum size of other formatted text does not exceed 1000 bytes. Also watch out for “%f”, which can print hundreds of digits for very large numbers.
//
// If a problem occurs, or sys.stdout is unset, the formatted message is written to the real (C level) stdout.
func PySys_WriteStdout(format string, args ...interface{}) {
//FIXME go-sprintf format and python-format may differ...
s := fmt.Sprintf(format, args...)
c_s := C.CString(s)
defer C.free(unsafe.Pointer(c_s))

//c_format := C.CString("%s")
//defer C.free(unsafe.Pointer(c_format))
//C._gopy_PySys_WriteStdout(c_s)

panic("not implemented")
}

// void PySys_WriteStderr(const char *format, ...)
// As above, but write to sys.stderr or stderr instead.
func PySys_WriteStderr(format string, args ...interface{}) {
//FIXME
panic("not implemented")
}

/////// Process Control /////////

// void Py_FatalError(const char *message)
Expand Down
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

.PHONY: all install test
.PHONY: all install test build

# default to gc, but allow caller to override on command line
GO_COMPILER:=$(GC)
Expand All @@ -26,3 +26,12 @@ install:

test: install
$(test_cwd)

build-py2: install
go build -buildmode=plugin ./python2/plugin/python2.go

build-py3: install
go build -buildmode=plugin ./python3/plugin/python3.go

build: build-py2 build-py3
go build ./cmd/go-python
7 changes: 0 additions & 7 deletions cgoflags.go

This file was deleted.

47 changes: 47 additions & 0 deletions cmd/go-python/go-python.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// a go wrapper around py-main
package main

import (
"fmt"
"os"
"plugin"

"github.com/sbinet/go-python"
)

func loadRuntime(vers string) python.Runtime {
pl, err := plugin.Open(fmt.Sprintf("python%s.so", vers))
if err != nil {
panic(err)
}
s, err := pl.Lookup("Runtime")
if err != nil {
panic(err)
}
r, ok := s.(*python.Runtime)
if !ok {
panic(fmt.Errorf("unexpected type: %T", s))
}
return *r
}

func main() {
vers := "2"
if os.Getenv("GO_PYTHON") == "3" {
vers = "3"
}
r := loadRuntime(vers)
py := python.NewInterpreter(r)
err := py.Initialize(true)
if err != nil {
panic(err)
}
defer py.Close()

err = py.Main(os.Args)
if e, ok := err.(python.RunError); ok {
os.Exit(e.Code)
} else if err != nil {
panic(err)
}
}
19 changes: 0 additions & 19 deletions cmd/go-python/main.go

This file was deleted.

27 changes: 27 additions & 0 deletions errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package python

import "fmt"

type RunError struct {
Code int
File string
}

func (e RunError) Error() string {
return fmt.Sprintf("python: error %d executing script %s", e.Code, e.File)
}

func errCode(ret int) error {
if ret == 0 {
return nil
}
return Error{Code: ret}
}

type Error struct {
Code int
}

func (e Error) Error() string {
return fmt.Sprintf("python: C-Python error code %d", e.Code)
}
21 changes: 21 additions & 0 deletions eval.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package python

import "github.com/sbinet/go-python/runtime"

type TraceFunc func(frame *Frame, what runtime.TraceType, arg runtime.Object)

func (py *Interpreter) Trace(fnc TraceFunc) {
var obj runtime.Object // TODO: unique value
py.r.EvalSetTrace(func(_ runtime.Object, frame runtime.Frame, what runtime.TraceType, arg runtime.Object) int {
fnc(&Frame{ptr: frame}, what, arg)
return 0
}, obj)
}

func (py *Interpreter) GetFrame() *Frame {
f := py.r.EvalGetFrame()
if f == nil {
return nil
}
return &Frame{ptr: f}
}
Loading