diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/.travis.yml b/.travis.yml index 678d6f3..a13c9f6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go go: - - 1.9.x - - 1.10.x + - 1.14.x + - 1.13.x before_install: - sudo apt-get update -qq diff --git a/README.md b/README.md index 11e7ac3..2e82335 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,12 @@ go-python ========= -[![Build Status](https://drone.io/github.com/sbinet/go-python/status.png)](https://drone.io/github.com/sbinet/go-python/latest) +**`sbinet/go-python` only supports CPython2. CPython2 isn't supported anymore by [python.org](https://python.org). Thus, `sbinet/go-python` is now archived.** +**A possible alternative may be to use and contribute to [go-python/cpy3](https://github.com/go-python/cpy3) instead.** + +[![Build Status](https://travis-ci.org/sbinet/go-python.svg?branch=master)](https://travis-ci.org/sbinet/go-python) +[![Build status](https://ci.appveyor.com/api/projects/status/n0ujg8no487a89vo/branch/master?svg=true)](https://ci.appveyor.com/project/sbinet/go-python/branch/master) +[![GoDocs](https://godocs.io/github.com/sbinet/go-python?status.svg)](https://godocs.io/github.com/sbinet/go-python) Naive `go` bindings towards the C-API of CPython-2. @@ -47,9 +52,9 @@ If ``go get`` + ``pkg-config`` failed: Documentation ------------- -Is available on ``godoc``: +Is available on ``godocs``: - http://godoc.org/github.com/sbinet/go-python + https://godocs.io/github.com/sbinet/go-python Example: diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..ad332c1 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,29 @@ +build: off + +clone_folder: c:\gopath\src\github.com\sbinet\go-python + +cache: + - '%LocalAppData%\\go-build' + - '%LocalAppData%\\pip' + +branches: + only: + - master + +environment: + GOPATH: c:\gopath + GOPY_APPVEYOR_CI: '1' + GODEBUG: 'cgocheck=0' + GOTRACEBACK: 'crash' + PYTHONUNBUFFERED: "1" + CPYTHON2DIR: "C:\\Python27-x64" + PATH: '%GOPATH%\bin;%CPYTHON2DIR%;%CPYTHON2DIR%\\Scripts;C:\msys64\mingw64\bin;C:\msys64\usr\bin\;%PATH%' + +stack: go 1.13 + +build_script: + - "%CPYTHON2DIR%\\python --version" + - go get -v -t ./... + +test_script: + - go test ./... diff --git a/capi.go b/capi.go index 2d8d777..deec35f 100644 --- a/capi.go +++ b/capi.go @@ -16,11 +16,13 @@ func Py_BuildValue(format string, args ...interface{}) *PyObject { // ml_doc char * points to the contents of the docstring type PyMethodDef struct { Name string // name of the method - Meth func(self, args *PyObject) *PyObject + Meth PyCFunction Flags MethodDefFlags Doc string } +type PyCFunction C.PyCFunction + type MethodDefFlags int const ( diff --git a/cgoflags.go b/cgoflags_unix.go similarity index 81% rename from cgoflags.go rename to cgoflags_unix.go index 5f1f0fa..4a38570 100644 --- a/cgoflags.go +++ b/cgoflags_unix.go @@ -1,7 +1,7 @@ +// +build !windows + package python // #cgo pkg-config: python-2.7 // #include "go-python.h" import "C" - -// EOF diff --git a/cgoflags_windows.go b/cgoflags_windows.go new file mode 100644 index 0000000..cb02cf2 --- /dev/null +++ b/cgoflags_windows.go @@ -0,0 +1,12 @@ +// +build windows + +package python + +// #cgo 386 CFLAGS: -I C:/Python27/include +// #cgo amd64 CFLAGS: -I C:/Python27-x64/include +// #cgo amd64 CFLAGS: -D MS_WIN64 +// #cgo 386 LDFLAGS: -L C:/Python27/libs -lpython27 +// #cgo amd64 LDFLAGS: -L C:/Python27-x64/libs -lpython27 +// +// #include "go-python.h" +import "C" diff --git a/file.go b/file.go index 983ebe8..2d2c5b4 100644 --- a/file.go +++ b/file.go @@ -1,8 +1,8 @@ package python /* -#include #include "go-python.h" +#include PyObject* _gopy_PyFile_FromFile(int fd, char *name, char *mode) { diff --git a/gen-cgoflags.go b/gen-cgoflags.go new file mode 100644 index 0000000..ce86c5c --- /dev/null +++ b/gen-cgoflags.go @@ -0,0 +1,141 @@ +// +build ignore + +// simple command to generate CGOFLAGS for a given python VM +package main + +import ( + "bytes" + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "log" + "os" + "os/exec" + "runtime" + "strings" + + "github.com/pkg/errors" +) + +func main() { + vm := flag.String("vm", "python", "path to a python VM") + flag.Parse() + + if *vm == "" { + log.Fatalf("need a python VM") + } + + cfg, err := getPythonConfig(*vm) + if err != nil { + log.Fatalf("could not infer python configuration: %v", err) + } + + oname := "cgoflags_unix.go" + switch runtime.GOOS { + case "windows": + oname = "cgoflags_windows.go" + } + + err = ioutil.WriteFile(oname, []byte(fmt.Sprintf(tmpl, + cfg.cflags, + cfg.ldflags, + runtime.GOOS, + )), 0644) + if err != nil { + log.Fatalf("could not write %q: %v", oname, err) + } +} + +// getPythonConfig returns the needed python configuration for the given +// python VM (python, python2, python3, pypy, etc...) +func getPythonConfig(vm string) (pyconfig, error) { + code := `import sys +import distutils.sysconfig as ds +import json +print(ds.get_config_vars()) +print(ds.get_python_lib()) +print(json.dumps({ + "version": sys.version_info.major, + "prefix": ds.get_config_var("prefix"), + "incdir": ds.get_python_inc(), + "libdir": ds.get_config_var("LIBDIR"), + "libdest": ds.get_config_var("LIBDEST"), + "libpy": ds.get_config_var("LIBRARY"), + "shlibs": ds.get_config_var("SHLIBS"), + "syslibs": ds.get_config_var("SYSLIBS"), + "shlinks": ds.get_config_var("LINKFORSHARED"), + "DLLs": ds.get_config_var("DLLLIBRARY"), +})) +` + + var cfg pyconfig + bin, err := exec.LookPath(vm) + if err != nil { + return cfg, errors.Wrapf(err, "could not locate python vm %q", vm) + } + + buf := new(bytes.Buffer) + cmd := exec.Command(bin, "-c", code) + cmd.Stdin = os.Stdin + cmd.Stdout = buf + cmd.Stderr = os.Stderr + err = cmd.Run() + if err != nil { + return cfg, errors.Wrap(err, "could not run python-config script") + } + + log.Printf("distutils:\n%s", buf.String()) + + var raw struct { + Version int `json:"version"` + IncDir string `json:"incdir"` + LibDir string `json:"libdir"` + LibPy string `json:"libpy"` + ShLibs string `json:"shlibs"` + SysLibs string `json:"syslibs"` + } + err = json.NewDecoder(buf).Decode(&raw) + if err != nil { + return cfg, errors.Wrapf(err, "could not decode JSON script output") + } + + if strings.HasSuffix(raw.LibPy, ".a") { + raw.LibPy = raw.LibPy[:len(raw.LibPy)-len(".a")] + } + if strings.HasPrefix(raw.LibPy, "lib") { + raw.LibPy = raw.LibPy[len("lib"):] + } + + cfg.version = raw.Version + cfg.cflags = strings.Join([]string{ + "-I " + raw.IncDir, + }, " ") + cfg.ldflags = strings.Join([]string{ + "-L " + raw.LibDir, + "-l " + raw.LibPy, + raw.ShLibs, + raw.SysLibs, + }, " ") + + return cfg, nil +} + +type pyconfig struct { + version int + cflags string + ldflags string +} + +const tmpl = `// Automatically generated. Do not edit. + +// +build %[3]s + +package python + +// #cgo %[3]s CFLAGS: %[1]s +// #cgo %[3]s LDFLAGS: %[2]s +// +// #include "go-python.h" +import "C" +` diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..8dc3bb5 --- /dev/null +++ b/go.mod @@ -0,0 +1,3 @@ +module github.com/sbinet/go-python + +go 1.13 diff --git a/heap.go b/heap.go index d3a00a1..d5f0c27 100644 --- a/heap.go +++ b/heap.go @@ -24,7 +24,7 @@ func cpyMethodDefs(name string, methods []PyMethodDef) *C.PyMethodDef { for i, meth := range methods { cmeth := C.PyMethodDef{ ml_name: C.CString(meth.Name), - ml_meth: (C.PyCFunction)(unsafe.Pointer(&meth.Meth)), + ml_meth: C.PyCFunction(meth.Meth), ml_flags: C.int(meth.Flags), ml_doc: C.CString(meth.Doc), } @@ -43,6 +43,7 @@ func Py_InitModule(name string, methods []PyMethodDef) (*PyObject, error) { obj := togo(C._gopy_InitModule(c_mname, cmeths)) if obj == nil { + PyErr_Print() return nil, errors.New("python: internal error; module creation failed.") } return obj, nil @@ -59,6 +60,7 @@ func Py_InitModule3(name string, methods []PyMethodDef, doc string) (*PyObject, obj := togo(C._gopy_InitModule3(cname, cmeths, cdoc)) if obj == nil { + PyErr_Print() return nil, errors.New("python: internal error; module creation failed.") } return obj, nil diff --git a/none.go b/none.go index 914899e..427c4cf 100644 --- a/none.go +++ b/none.go @@ -6,6 +6,6 @@ import "C" // The Python None object, denoting lack of value. This object has no methods. // It needs to be treated just like any other object with respect to reference // counts. -var Py_None = togo(C._gopy_pynone()) +var Py_None = &PyObject{ptr: C._gopy_pynone()} // EOF diff --git a/numeric.go b/numeric.go index 31a2c2a..97125ad 100644 --- a/numeric.go +++ b/numeric.go @@ -332,13 +332,13 @@ func PyBool_Check(self *PyObject) bool { // The Python False object. This object has no methods. // It needs to be treated just like any other object with respect to // reference counts. -var Py_False = togo(C._gopy_pyfalse()) +var Py_False = &PyObject{ptr: C._gopy_pyfalse()} // PyObject* Py_True // The Python True object. This object has no methods. // It needs to be treated just like any other object with respect to // reference counts. -var Py_True = togo(C._gopy_pytrue()) +var Py_True = &PyObject{ptr: C._gopy_pytrue()} /* Py_RETURN_FALSE diff --git a/object.go b/object.go index 274fcfd..2e78c2d 100644 --- a/object.go +++ b/object.go @@ -15,6 +15,13 @@ type PyObject struct { ptr *C.PyObject } +// String returns a string representation of the PyObject +func (self *PyObject) String() string { + o := self.Str() + defer o.DecRef() + return PyString_AsString(o) +} + func (self *PyObject) topy() *C.PyObject { return self.ptr } @@ -27,10 +34,18 @@ func topy(self *PyObject) *C.PyObject { } func togo(obj *C.PyObject) *PyObject { - if obj == nil { + switch obj { + case nil: return nil + case Py_None.ptr: + return Py_None + case Py_True.ptr: + return Py_True + case Py_False.ptr: + return Py_False + default: + return &PyObject{ptr: obj} } - return &PyObject{ptr: obj} } // PyObject_FromVoidPtr converts a PyObject from an unsafe.Pointer diff --git a/python_test.go b/python_test.go index 0adeb4e..564b64e 100644 --- a/python_test.go +++ b/python_test.go @@ -112,7 +112,7 @@ func TestErrFetch(t *testing.T) { t.Parallel() testPkg(t, pkg{ path: "tests/errfetch", - want: []byte("exc=&{}\nval=&{}\ntb=&{}\n"), + want: []byte("exc=\nval=\ntb=\n"), }) } @@ -140,3 +140,12 @@ func TestIssue61(t *testing.T) { `), }) } + +func TestCheckNone(t *testing.T) { + t.Parallel() + testPkg(t, pkg{ + path: "tests/none-check", + want: []byte(`type=, str=None, eq_none=true +`), + }) +} diff --git a/tests/none-check/get_none.py b/tests/none-check/get_none.py new file mode 100644 index 0000000..7d27c14 --- /dev/null +++ b/tests/none-check/get_none.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python2 + +def get_none(): + return None + diff --git a/tests/none-check/main.go b/tests/none-check/main.go new file mode 100644 index 0000000..990fec4 --- /dev/null +++ b/tests/none-check/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "fmt" + "log" + + "github.com/sbinet/go-python" +) + +func init() { + err := python.Initialize() + if err != nil { + log.Panic(err) + } +} + +func main() { + module := python.PyImport_ImportModule("get_none") + if module == nil { + log.Fatal("could not import 'get_none' module") + } + get_none := module.GetAttrString("get_none") + if get_none == nil { + log.Fatal("could not import 'get_none' function") + } + none := get_none.CallFunction() + fmt.Printf("type=%s, str=%s, eq_none=%t\n", + python.PyString_AsString(none.Type().Str()), + python.PyString_AsString(none.Str()), + none == python.Py_None, + ) +} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy