├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CONTRIBUTE.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── SUPPORT_MATRIX.md ├── _examples ├── arrays │ ├── arrays.go │ └── test.py ├── cgo │ ├── cgo.go │ └── test.py ├── consts │ ├── consts.go │ └── test.py ├── cpkg │ ├── cpkg.go │ └── run.go ├── cstrings │ ├── cstrings.go │ └── test.py ├── empty │ ├── empty.go │ └── test.py ├── funcs │ ├── __pycache__ │ │ └── test.cpython-36.pyc │ ├── funcs.go │ └── test.py ├── gobytes │ ├── gobytes.go │ └── test.py ├── gopyerrors │ ├── gopyerrors.go │ └── test.py ├── gopygc │ ├── gopygc.go │ └── test.py ├── gostrings │ ├── strings.go │ └── test.py ├── hi │ ├── hi.go │ └── test.py ├── iface │ ├── iface.go │ └── test.py ├── lot │ ├── lot.go │ └── test.py ├── maps │ ├── maps.go │ └── test.py ├── named │ ├── named.go │ └── test.py ├── osfile │ ├── osfile.go │ └── test.py ├── package │ └── mypkg │ │ ├── mypkg.go │ │ └── test.py ├── pkgconflict │ ├── html.go │ ├── test.py │ └── text.go ├── pointers │ ├── pointers.go │ └── test.py ├── pyerrors │ ├── pyerrors.go │ └── test.py ├── rename │ ├── rename.go │ └── test.py ├── seqs │ ├── seqs.go │ └── test.py ├── simple │ ├── simple.go │ └── test.py ├── sliceptr │ ├── sliceptr.go │ └── test.py ├── slices │ ├── slices.go │ └── test.py ├── structs │ ├── structs.go │ └── test.py ├── unicode │ ├── encoding.go │ └── test.py ├── variadic │ ├── test.py │ └── variadic.go ├── vars │ ├── test.py │ └── vars.go └── wrapper │ ├── pywrapper │ └── wrapper_code.go │ └── real_code.go ├── appveyor.yml ├── bind ├── bind.go ├── doc.go ├── gen.go ├── gen_func.go ├── gen_map.go ├── gen_slice.go ├── gen_struct.go ├── gen_type.go ├── gen_varconst.go ├── package.go ├── printer.go ├── printer_test.go ├── stdtypes.go ├── symbols.go ├── types.go ├── utils.go └── utils_test.go ├── cmd_build.go ├── cmd_exe.go ├── cmd_gen.go ├── cmd_pkg.go ├── dirs.go ├── doc.go ├── gen.go ├── go.mod ├── go.sum ├── gopy.py ├── gopyh └── handle.go ├── main.go ├── main_darwin.go ├── main_test.go ├── main_unix.go ├── main_unix_test.go ├── main_windows.go ├── main_windows_test.go ├── pkgsetup.go ├── python.go └── version.go /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | TAGS: "-tags=ci" 11 | COVERAGE: "-coverpkg=github.com/go-python/gopy/..." 12 | # Init() in main_test will make sure all backends are available if 13 | # GOPY_TRAVIS_CI is set 14 | GOPY_TRAVIS_CI: 1 15 | GOTRACEBACK: crash 16 | GO111MODULE: auto 17 | 18 | jobs: 19 | 20 | build: 21 | name: Build 22 | strategy: 23 | matrix: 24 | go-version: [1.22.x, 1.21.x] 25 | platform: [ubuntu-latest] 26 | #platform: [ubuntu-latest, macos-latest, windows-latest] 27 | runs-on: ${{ matrix.platform }} 28 | steps: 29 | - name: Install Go 30 | uses: actions/setup-go@v2 31 | with: 32 | go-version: ${{ matrix.go-version }} 33 | 34 | - name: Cache-Go 35 | uses: actions/cache@v1 36 | with: 37 | path: | 38 | ~/go/pkg/mod # Module download cache 39 | ~/.cache/go-build # Build cache (Linux) 40 | ~/Library/Caches/go-build # Build cache (Mac) 41 | '%LocalAppData%\go-build' # Build cache (Windows) 42 | 43 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 44 | restore-keys: | 45 | ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} 46 | 47 | - name: Checkout code 48 | uses: actions/checkout@v2 49 | 50 | - name: Install Linux packages 51 | if: matrix.platform == 'ubuntu-latest' 52 | run: | 53 | sudo apt-get update 54 | sudo apt-get install curl libffi-dev python3-cffi python3-pip 55 | # install pybindgen 56 | python3 -m pip install --user -U pybindgen 57 | # install goimports 58 | go install golang.org/x/tools/cmd/goimports@latest 59 | 60 | 61 | - name: Build-Linux 62 | if: matrix.platform == 'ubuntu-latest' 63 | run: | 64 | make 65 | - name: Test Linux 66 | if: matrix.platform == 'ubuntu-latest' 67 | run: | 68 | make test 69 | - name: Upload-Coverage 70 | if: matrix.platform == 'ubuntu-latest' 71 | uses: codecov/codecov-action@v1 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE files 2 | .idea/ 3 | *.iml 4 | -------------------------------------------------------------------------------- /CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | The `go-python` project (and `gopy`) eagerly accepts contributions from the community. 4 | 5 | ## Introduction 6 | 7 | 8 | The `go-python` project provides libraries and tools in Go for the Go community to better integrate with Python projects and libraries, and we would like you to join us in improving `go-python`'s quality and scope. 9 | This document is for contributors or those interested in contributing. 10 | Questions about `go-python` and the use of its libraries can be directed to the [go-python](mailto:go-python@googlegroups.com) mailing list. 11 | 12 | ## Contributing 13 | 14 | ### Working Together 15 | 16 | When contributing or otherwise participating, please: 17 | 18 | - Be friendly and welcoming 19 | - Be patient 20 | - Be thoughtful 21 | - Be respectful 22 | - Be charitable 23 | - Avoid destructive behavior 24 | 25 | Excerpted from the [Go conduct document](https://golang.org/conduct). 26 | 27 | ### Reporting Bugs 28 | 29 | When you encounter a bug, please open an issue on the corresponding repository. 30 | Start the issue title with the repository/sub-repository name, like `bind: issue name`. 31 | Be specific about the environment you encountered the bug in (_e.g.:_ operating system, Go compiler version, ...). 32 | If you are able to write a reproducer for the bug, please include it in the issue. 33 | As a rule, we keep all tests OK and try to increase code coverage. 34 | 35 | ### Suggesting Enhancements 36 | 37 | If the scope of the enhancement is small, open an issue. 38 | If it is large, such as suggesting a new repository, sub-repository, or interface refactoring, then please start a discussion on [the go-python list](https://groups.google.com/forum/#!forum/go-python). 39 | 40 | ### Your First Code Contribution 41 | 42 | If you are a new contributor, *thank you!* 43 | Before your first merge, you will need to be added to the [CONTRIBUTORS](https://github.com/go-python/license/blob/master/CONTRIBUTORS) and [AUTHORS](https://github.com/go-python/license/blob/master/AUTHORS) files. 44 | Open a pull request adding yourself to these files. 45 | All `go-python` code follows the BSD license in the [license document](https://github.com/go-python/license/blob/master/LICENSE). 46 | We prefer that code contributions do not come with additional licensing. 47 | For exceptions, added code must also follow a BSD license. 48 | 49 | ### Code Contribution 50 | 51 | If it is possible to split a large pull request into two or more smaller pull requests, please try to do so. 52 | Pull requests should include tests for any new code before merging. 53 | It is ok to start a pull request on partially implemented code to get feedback, and see if your approach to a problem is sound. 54 | You don't need to have tests, or even have code that compiles to open a pull request, although both will be needed before merge. 55 | When tests use magic numbers, please include a comment explaining the source of the number. 56 | Benchmarks are optional for new features, but if you are submitting a pull request justified by performance improvement, you will need benchmarks to measure the impact of your change, and the pull request should include a report from [benchcmp](https://godoc.org/golang.org/x/tools/cmd/benchcmp) or, preferably, [benchstat](https://godoc.org/golang.org/x/perf/cmd/benchstat). 57 | 58 | Commit messages also follow some rules. 59 | They are best explained at the official [Go](https://golang.org) "Contributing guidelines" document: 60 | 61 | [golang.org/doc/contribute.html](https://golang.org/doc/contribute.html#commit_changes) 62 | 63 | For example: 64 | 65 | ``` 66 | bind: add support for cffi 67 | 68 | This CL adds support for the cffi python backend. 69 | The existing implementation, cpython, only generates code for the 70 | CPython-2 C API. 71 | Now, with cffi, we support generation of python modules for Python-2, 72 | Python-3 and PyPy VMs. 73 | 74 | Fixes go-python/gopy#42. 75 | ``` 76 | 77 | If the `CL` modifies multiple packages at the same time, include them in the commit message: 78 | 79 | ``` 80 | py,bind: implement wrapping of Go interfaces 81 | 82 | bla-bla 83 | 84 | Fixes go-python/gopy#40. 85 | ``` 86 | 87 | Please always format your code with [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports). 88 | Best is to have it invoked as a hook when you save your `.go` files. 89 | 90 | Files in the `go-python` repository don't list author names, both to avoid clutter and to avoid having to keep the lists up to date. 91 | Instead, your name will appear in the change log and in the [CONTRIBUTORS](https://github.com/go-python/license/blob/master/CONTRIBUTORS) and [AUTHORS](https://github.com/go-python/license/blob/master/AUTHORS) files. 92 | 93 | New files that you contribute should use the standard copyright header: 94 | 95 | ``` 96 | // Copyright 20xx The go-python Authors. All rights reserved. 97 | // Use of this source code is governed by a BSD-style 98 | // license that can be found in the LICENSE file. 99 | ``` 100 | 101 | Files in the repository are copyright the year they are added. 102 | Do not update the copyright year on files that you change. 103 | 104 | ### Code Review 105 | 106 | If you are a contributor, please be welcoming to new contributors. 107 | [Here](http://sarah.thesharps.us/2014/09/01/the-gentle-art-of-patch-review/) is a good guide. 108 | 109 | There are several terms code reviewers may use that you should become familiar with. 110 | 111 | * ` LGTM ` — looks good to me 112 | * ` SGTM ` — sounds good to me 113 | * ` PTAL ` — please take another look 114 | * ` CL ` — change list; a single commit in the repository 115 | * ` s/foo/bar/ ` — please replace ` foo ` with ` bar `; this is [sed syntax](http://en.wikipedia.org/wiki/Sed#Usage) 116 | * ` s/foo/bar/g ` — please replace ` foo ` with ` bar ` throughout your entire change 117 | 118 | We follow the convention of requiring at least 1 reviewer to say LGTM before a merge. 119 | When code is tricky or controversial, submitters and reviewers can request additional review from others and more LGTMs before merge. 120 | You can ask for more review by saying PTAL in a comment in a pull request. 121 | You can follow a PTAL with one or more @someone to get the attention of particular people. 122 | If you don't know who to ask, and aren't getting enough review after saying PTAL, then PTAL @go-python/developers will get more attention. 123 | Also note that you do not have to be the pull request submitter to request additional review. 124 | 125 | ### What Can I Do to Help? 126 | 127 | If you are looking for some way to help the `go-python` project, there are good places to start, depending on what you are comfortable with. 128 | 129 | - You can [search](https://github.com/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue+user%3Ago-python) for open issues in need of resolution. 130 | - You can improve documentation, or improve examples. 131 | - You can add and improve tests. 132 | - You can improve performance, either by improving accuracy, speed, or both. 133 | - You can suggest and implement new features that you think belong in `go-python`. 134 | 135 | ### Style 136 | 137 | We use [Go style](https://github.com/golang/go/wiki/CodeReviewComments). 138 | 139 | --- 140 | 141 | This _"Contributing"_ guide has been extracted from the [Gonum](https://gonum.org) project. 142 | Its guide is [here](https://github.com/gonum/license/blob/master/CONTRIBUTING.md). 143 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:onbuild 2 | RUN apt-get update && apt-get install -y pkg-config python2.7-dev && apt-get clean 3 | CMD /go/bin/app 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright ©2015 The go-python Authors. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | * Redistributions of source code must retain the above copyright 6 | notice, this list of conditions and the following disclaimer. 7 | * Redistributions in binary form must reproduce the above copyright 8 | notice, this list of conditions and the following disclaimer in the 9 | documentation and/or other materials provided with the distribution. 10 | * Neither the name of the gonum project nor the names of its authors and 11 | contributors may be used to endorse or promote products derived from this 12 | software without specific prior written permission. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Basic Go makefile 2 | 3 | GOCMD=go 4 | GOBUILD=$(GOCMD) build 5 | GOCLEAN=$(GOCMD) clean 6 | GOTEST=$(GOCMD) test 7 | GOGET=$(GOCMD) get 8 | 9 | DIRS=`go list ./...` 10 | 11 | PYTHON=python3 12 | PIP=$(PYTHON) -m pip 13 | 14 | all: build 15 | 16 | build: 17 | @echo "GO111MODULE = $(value GO111MODULE)" 18 | $(GOBUILD) -v $(DIRS) 19 | 20 | test: 21 | @echo "GO111MODULE = $(value GO111MODULE)" 22 | $(GOTEST) -v $(DIRS) 23 | 24 | clean: 25 | @echo "GO111MODULE = $(value GO111MODULE)" 26 | $(GOCLEAN) ./... 27 | 28 | fmts: 29 | gofmt -s -w . 30 | 31 | vet: 32 | @echo "GO111MODULE = $(value GO111MODULE)" 33 | $(GOCMD) vet $(DIRS) | grep -v unkeyed 34 | 35 | tidy: export GO111MODULE = on 36 | tidy: 37 | @echo "GO111MODULE = $(value GO111MODULE)" 38 | go mod tidy 39 | 40 | mod-update: export GO111MODULE = on 41 | mod-update: 42 | @echo "GO111MODULE = $(value GO111MODULE)" 43 | go get -u ./... 44 | go mod tidy 45 | 46 | prereq: 47 | @echo "Installing python prerequisites -- ignore err if already installed:" 48 | - $(PIP) install -r requirements.txt 49 | @echo 50 | @echo "if this fails, you may see errors like this:" 51 | @echo " Undefined symbols for architecture x86_64:" 52 | @echo " _PyInit__gi, referenced from:..." 53 | @echo 54 | 55 | 56 | # NOTE: MUST update version number here prior to running 'make release' and edit this file! 57 | VERS=v0.4.10 58 | PACKAGE=main 59 | GIT_COMMIT=`git rev-parse --short HEAD` 60 | VERS_DATE=`date -u +%Y-%m-%d\ %H:%M` 61 | VERS_FILE=version.go 62 | 63 | release: 64 | /bin/rm -f $(VERS_FILE) 65 | @echo "// WARNING: auto-generated by Makefile release target -- run 'make release' to update" > $(VERS_FILE) 66 | @echo "" >> $(VERS_FILE) 67 | @echo "package $(PACKAGE)" >> $(VERS_FILE) 68 | @echo "" >> $(VERS_FILE) 69 | @echo "const (" >> $(VERS_FILE) 70 | @echo " Version = \"$(VERS)\"" >> $(VERS_FILE) 71 | @echo " GitCommit = \"$(GIT_COMMIT)\" // the commit JUST BEFORE the release" >> $(VERS_FILE) 72 | @echo " VersionDate = \"$(VERS_DATE)\" // UTC" >> $(VERS_FILE) 73 | @echo ")" >> $(VERS_FILE) 74 | @echo "" >> $(VERS_FILE) 75 | goimports -w $(VERS_FILE) 76 | /bin/cat $(VERS_FILE) 77 | git commit -am "$(VERS) release" 78 | git tag -a $(VERS) -m "$(VERS) release" 79 | git push 80 | git push origin --tags 81 | 82 | -------------------------------------------------------------------------------- /SUPPORT_MATRIX.md: -------------------------------------------------------------------------------- 1 | # Support matrix 2 | 3 | NOTE: File auto-generated by TestCheckSupportMatrix in main_test.go. Please 4 | don't modify manually. 5 | 6 | Feature |py3 7 | --- | --- 8 | _examples/arrays | yes 9 | _examples/cgo | yes 10 | _examples/consts | yes 11 | _examples/cstrings | yes 12 | _examples/empty | yes 13 | _examples/funcs | yes 14 | _examples/gobytes | yes 15 | _examples/gopygc | yes 16 | _examples/gostrings | yes 17 | _examples/hi | yes 18 | _examples/iface | yes 19 | _examples/lot | yes 20 | _examples/maps | yes 21 | _examples/named | yes 22 | _examples/osfile | yes 23 | _examples/pkgconflict | yes 24 | _examples/pointers | yes 25 | _examples/pyerrors | yes 26 | _examples/rename | yes 27 | _examples/seqs | yes 28 | _examples/simple | yes 29 | _examples/sliceptr | yes 30 | _examples/slices | yes 31 | _examples/structs | yes 32 | _examples/unicode | yes 33 | _examples/variadic | yes 34 | _examples/vars | yes 35 | -------------------------------------------------------------------------------- /_examples/arrays/arrays.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package arrays 6 | 7 | func IntSum(a [4]int) int { 8 | sum := 0 9 | for i := 0; i < len(a); i++ { 10 | sum += a[i] 11 | } 12 | return sum 13 | } 14 | 15 | func CreateArray() [4]int { 16 | return [4]int{1, 2, 3, 4} 17 | } 18 | -------------------------------------------------------------------------------- /_examples/arrays/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from __future__ import print_function 6 | import arrays 7 | 8 | a = [1,2,3,4] 9 | b = arrays.CreateArray() 10 | print ("Python list:", a) 11 | print ("Go array: ", b) 12 | # note: only support slices as converted from python -- arrays don't make sense 13 | #print ("arrays.IntSum from Python list:", arrays.IntSum(a)) 14 | print ("arrays.IntSum from Go array:", arrays.IntSum(b)) 15 | 16 | print("OK") 17 | -------------------------------------------------------------------------------- /_examples/cgo/cgo.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package cgo tests bindings of CGo-based packages. 6 | package cgo 7 | 8 | //#include 9 | //#include 10 | //#include 11 | //const char* cpkg_sprintf(const char *str) { 12 | // char *o = (char*)malloc(strlen(str)); 13 | // sprintf(o, "%s", str); 14 | // return o; 15 | //} 16 | import "C" 17 | 18 | import ( 19 | "fmt" 20 | "unsafe" 21 | ) 22 | 23 | // Hi returns a string from Go (via C's stdio) 24 | func Hi() string { 25 | cstr := C.CString("hi from go\n") 26 | defer C.free(unsafe.Pointer(cstr)) 27 | cout := C.cpkg_sprintf(cstr) 28 | defer C.free(unsafe.Pointer(cout)) 29 | return C.GoString(cout) 30 | } 31 | 32 | // Hello returns a string via C's stdio 33 | func Hello(s string) string { 34 | if s == "" { 35 | s = "you" 36 | } 37 | cstr := C.CString(fmt.Sprintf("hello %s from go\n", s)) 38 | defer C.free(unsafe.Pointer(cstr)) 39 | cout := C.cpkg_sprintf(cstr) 40 | defer C.free(unsafe.Pointer(cout)) 41 | return C.GoString(cout) 42 | } 43 | -------------------------------------------------------------------------------- /_examples/cgo/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import cgo 9 | 10 | print("cgo.doc: %s" % repr(cgo.__doc__).lstrip('u')) 11 | print("cgo.Hi()= %s" % repr(cgo.Hi()).lstrip('u')) 12 | print("cgo.Hello(you)= %s" % repr(cgo.Hello("you")).lstrip('u')) 13 | 14 | print("OK") 15 | -------------------------------------------------------------------------------- /_examples/consts/consts.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package consts 6 | 7 | const ( 8 | C1 = "c1" 9 | C2 = 42 10 | C3 = 666.666 11 | ) 12 | 13 | const ( 14 | C4 string = "c4" 15 | C5 int = 42 16 | C6 uint = 42 17 | C7 float64 = 666.666 18 | ) 19 | 20 | type Kind int 21 | 22 | const ( 23 | Kind1 Kind = 1 24 | Kind2 = 2 25 | ) 26 | 27 | // FIXME: also use an unexported type 28 | // type kind int 29 | // const ( 30 | // Kind3 kind = 3 31 | // Kind4 = 4 32 | // ) 33 | -------------------------------------------------------------------------------- /_examples/consts/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import consts 9 | 10 | print("c1 = %s" % consts.C1) 11 | print("c2 = %s" % consts.C2) 12 | print("c3 = %s" % consts.C3) 13 | print("c4 = %s" % consts.C4) 14 | print("c5 = %s" % consts.C5) 15 | print("c6 = %s" % consts.C6) 16 | print("c7 = %s" % consts.C7) 17 | 18 | print("k1 = %s" % consts.Kind1) 19 | print("k2 = %s" % consts.Kind2) 20 | ## FIXME: unexported types not supported yet (issue #44) 21 | #print("k3 = %s" % consts.Kind3) 22 | #print("k4 = %s" % consts.Kind4) 23 | 24 | print("OK") 25 | -------------------------------------------------------------------------------- /_examples/cpkg/cpkg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cpkg 6 | 7 | //#include 8 | //#include 9 | //#include 10 | //static inline void cpkg_printf(const char *str) { 11 | // fprintf(stdout, "%s", str); 12 | // fflush(stdout); 13 | //} 14 | import "C" 15 | 16 | import ( 17 | "fmt" 18 | "unsafe" 19 | ) 20 | 21 | // Hi prints hi from Go (via C's stdio) 22 | func Hi() { 23 | cstr := C.CString("hi from go\n") 24 | defer C.free(unsafe.Pointer(cstr)) 25 | C.cpkg_printf(cstr) 26 | } 27 | 28 | // Hello prints a string via C's stdio 29 | func Hello(s string) { 30 | if s == "" { 31 | s = "you" 32 | } 33 | cstr := C.CString(fmt.Sprintf("hello %s from go\n", s)) 34 | defer C.free(unsafe.Pointer(cstr)) 35 | C.cpkg_printf(cstr) 36 | } 37 | 38 | // Printf prints a string via C's stdio 39 | func Printf(format string, args ...interface{}) { 40 | str := fmt.Sprintf(format, args...) 41 | cstr := C.CString(str) 42 | defer C.free(unsafe.Pointer(cstr)) 43 | C.cpkg_printf(cstr) 44 | } 45 | -------------------------------------------------------------------------------- /_examples/cpkg/run.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build ignore 6 | // +build ignore 7 | 8 | package main 9 | 10 | import ( 11 | "fmt" 12 | 13 | "github.com/go-python/gopy/_examples/cpkg" 14 | ) 15 | 16 | func main() { 17 | fmt.Printf("hello from go\n") 18 | cpkg.Hello("me") 19 | fmt.Printf("bye me\n") 20 | cpkg.Hello("you") 21 | fmt.Printf("bye you\n") 22 | } 23 | -------------------------------------------------------------------------------- /_examples/cstrings/cstrings.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package cstrings 6 | 7 | import "strings" 8 | 9 | func StringValue(s string, n int) string { 10 | return strings.Repeat(s, n) 11 | } 12 | 13 | type StructWithString struct { 14 | V string 15 | T int 16 | } 17 | 18 | type NestedStructWithString struct { 19 | A int 20 | S StructWithString 21 | } 22 | 23 | func StringInStruct(s string, n int) StructWithString { 24 | return StructWithString{ 25 | V: strings.Repeat(s, n), 26 | } 27 | } 28 | 29 | func StringInNestedStruct(s string, n int) NestedStructWithString { 30 | return NestedStructWithString{ 31 | A: 2, 32 | S: StructWithString{ 33 | V: strings.Repeat(s, n), 34 | }, 35 | } 36 | } 37 | 38 | func StringSlice(s string, n int) []string { 39 | return []string{ 40 | strings.Repeat(s, n), 41 | } 42 | } 43 | 44 | func StringMap(s string, n int) map[string]string { 45 | return map[string]string{ 46 | "a": strings.Repeat(s, n), 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /_examples/cstrings/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import cstrings 9 | import gc 10 | import resource 11 | 12 | verbose = False 13 | iterations = 10000 14 | size = 4096 15 | 16 | 17 | def gofnString(): 18 | return cstrings.StringValue("a", size) 19 | 20 | 21 | def gofnStruct(): 22 | s = cstrings.StringInStruct("a", size) 23 | return s.V 24 | 25 | 26 | def gofnNestedStruct(): 27 | s = cstrings.StringInNestedStruct("a", size) 28 | return s.S.V 29 | 30 | 31 | def gofnSlice(): 32 | s = cstrings.StringSlice("a", size) 33 | return s[0] 34 | 35 | 36 | def gofnMap(): 37 | m = cstrings.StringMap("a", size) 38 | return m["a"] 39 | 40 | 41 | def print_memory(s): 42 | m = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 43 | if verbose: 44 | print(s, m) 45 | return m 46 | 47 | 48 | def _run_fn(fn): 49 | memoryvals = [] 50 | t = [fn() for _ in range(iterations)] 51 | memoryvals.append(print_memory( 52 | "Memory usage after first list creation is:")) 53 | 54 | t = [fn() for _ in range(iterations)] 55 | memoryvals.append(print_memory( 56 | "Memory usage after second list creation is:")) 57 | 58 | gc.collect() 59 | memoryvals.append(print_memory("Memory usage after GC:")) 60 | 61 | t = [fn() for _ in range(iterations)] 62 | memoryvals.append(print_memory( 63 | "Memory usage after third list creation is:")) 64 | 65 | gc.collect() 66 | memoryvals.append(print_memory("Memory usage after GC:")) 67 | return memoryvals 68 | 69 | 70 | for fn in [gofnString, gofnStruct, gofnNestedStruct, gofnSlice, gofnMap]: 71 | alloced = size * iterations 72 | a = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 73 | pass1 = _run_fn(fn) 74 | b = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 75 | pass2 = _run_fn(fn) 76 | c = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 77 | if verbose: 78 | print(fn.__name__, pass1) 79 | print(fn.__name__, pass2) 80 | print(fn.__name__, a, b, c) 81 | 82 | print(fn.__name__, "leaked: ", (c-b) > (size * iterations)) 83 | 84 | # bump up the size of each successive test to ensure that leaks 85 | # are not absorbed by previous rss growth. 86 | size += 4096 87 | 88 | 89 | print("OK") 90 | -------------------------------------------------------------------------------- /_examples/empty/empty.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package empty does not expose anything. 6 | // We may want to wrap and import it just for its side-effects. 7 | package empty 8 | 9 | import "fmt" 10 | 11 | func init() { 12 | // todo: not sure why init is not being called! 13 | fmt.Printf("empty.init()... [CALLED]\n") 14 | } 15 | -------------------------------------------------------------------------------- /_examples/empty/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import empty as pkg 9 | 10 | print("doc(pkg):\n%s" % repr(pkg.__doc__).lstrip('u')) 11 | 12 | print("OK") 13 | -------------------------------------------------------------------------------- /_examples/funcs/__pycache__/test.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/go-python/gopy/5f285b890023153b3a17892ef7f04fe9a654bff2/_examples/funcs/__pycache__/test.cpython-36.pyc -------------------------------------------------------------------------------- /_examples/funcs/funcs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package funcs 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/go-python/gopy/_examples/cpkg" 11 | ) 12 | 13 | type FunStruct struct { 14 | FieldI int 15 | FieldS string 16 | } 17 | 18 | func (fs *FunStruct) CallBack(i int, fun func(fs *FunStruct, i int, s string)) { 19 | fun(fs, i, fs.FieldS) 20 | } 21 | 22 | type RecvFunc func(fs *FunStruct, i int, v interface{}) 23 | 24 | func (fs *FunStruct) CallBackIf(i int, fun RecvFunc) { 25 | fun(fs, i, fs.FieldS) 26 | } 27 | 28 | func (fs *FunStruct) CallBackRval(i int, fun func(fs *FunStruct, i int, v interface{}) bool) { 29 | rv := fun(fs, i, fs.FieldS) 30 | fmt.Printf("got return value: %v\n", rv) 31 | } 32 | 33 | func (fs *FunStruct) OtherMeth(i int, s string) { 34 | fs.FieldI = i 35 | fs.FieldS = s 36 | fmt.Printf("i=%d s=%s\n", i, s) 37 | } 38 | 39 | func (fs *FunStruct) ObjArg(ofs *FunStruct) { 40 | if ofs == nil { 41 | fmt.Printf("got nil\n") 42 | } else { 43 | fmt.Printf("ofs FieldI: %d FieldS: %s\n", ofs.FieldI, ofs.FieldS) 44 | } 45 | } 46 | 47 | var ( 48 | F1 func() 49 | F2 Func 50 | F3 S1 51 | F4 S2 52 | F5 []func() 53 | F6 []Func 54 | F7 [2]func() 55 | F8 [3]Func 56 | ) 57 | 58 | type Func func() 59 | 60 | type S1 struct { 61 | F1 Func 62 | F2 []Func 63 | F3 [4]Func 64 | } 65 | 66 | type S2 struct { 67 | F1 func() 68 | F2 []func() 69 | F3 [5]func() 70 | } 71 | 72 | func init() { 73 | F1 = func() { 74 | cpkg.Printf("calling F1\n") 75 | } 76 | 77 | F2 = Func(func() { 78 | cpkg.Printf("calling F2\n") 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /_examples/funcs/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import go, funcs 9 | 10 | fs = funcs.FunStruct() 11 | fs.FieldS = "str field" 12 | fs.FieldI = 42 13 | 14 | def cbfun(afs, ival, sval): 15 | tfs = funcs.FunStruct(handle=afs) 16 | print("in python cbfun: FieldI: ", tfs.FieldI, " FieldS: ", tfs.FieldS, " ival: ", ival, " sval: ", sval) 17 | 18 | def cbfunif(afs, ival, ifval): 19 | tfs = funcs.FunStruct(handle=afs) 20 | print("in python cbfunif: FieldI: ", tfs.FieldI, " FieldS: ", tfs.FieldS, " ival: ", ival, " ifval: ", ifval) 21 | 22 | def cbfunrval(afs, ival, ifval): 23 | tfs = funcs.FunStruct(handle=afs) 24 | print("in python cbfunrval: FieldI: ", tfs.FieldI, " FieldS: ", tfs.FieldS, " ival: ", ival, " ifval: ", ifval) 25 | return True 26 | 27 | class MyClass(go.GoClass): 28 | def __init__(self, *args, **kwargs): 29 | self.misc = 2 30 | 31 | def ClassFun(self, afs, ival, sval): 32 | tfs = funcs.FunStruct(handle=afs) 33 | print("in python class fun: FieldI: ", tfs.FieldI, " FieldS: ", tfs.FieldS, " ival: ", ival, " sval: ", sval) 34 | 35 | def CallSelf(self): 36 | fs.CallBack(77, self.ClassFun) 37 | 38 | print("fs.CallBack(22, cbfun)...") 39 | fs.CallBack(22, cbfun) 40 | 41 | print("fs.CallBackIf(22, cbfunif)...") 42 | fs.CallBackIf(22, cbfunif) 43 | 44 | print("fs.CallBackRval(22, cbfunrval)...") 45 | fs.CallBackRval(22, cbfunrval) 46 | 47 | cls = MyClass() 48 | 49 | # note: no special code needed to work with methods in callback (PyObject_CallObject just works) 50 | # BUT it does NOT work if the callback is initiated from a different thread! Then only regular 51 | # functions work. 52 | print("fs.CallBack(32, cls.ClassFun)...") 53 | fs.CallBack(32, cls.ClassFun) 54 | 55 | print("cls.CallSelf...") 56 | cls.CallSelf() 57 | 58 | 59 | print("fs.ObjArg with nil") 60 | fs.ObjArg(go.nil) 61 | 62 | print("fs.ObjArg with fs") 63 | fs.ObjArg(fs) 64 | 65 | # TODO: not currently supported: 66 | 67 | # print("funcs.F1()...") 68 | # f1 = funcs.F1() 69 | # print("f1()= %s" % f1()) 70 | # 71 | # print("funcs.F2()...") 72 | # f2 = funcs.F2() 73 | # print("f2()= %s" % f2()) 74 | # 75 | # print("s1 = funcs.S1()...") 76 | # s1 = funcs.S1() 77 | # print("s1.F1 = funcs.F2()...") 78 | # s1.F1 = funcs.F2() 79 | # print("s1.F1() = %s" % s1.F1()) 80 | # 81 | # print("s2 = funcs.S2()...") 82 | # s2 = funcs.S2() 83 | # print("s2.F1 = funcs.F1()...") 84 | # s2.F1 = funcs.F1() 85 | # print("s2.F1() = %s" % s2.F1()) 86 | 87 | print("OK") 88 | -------------------------------------------------------------------------------- /_examples/gobytes/gobytes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gobytes 6 | 7 | func HashBytes(b []byte) [4]byte { 8 | result := [4]byte{0, 0, 0, 0} 9 | full_blocks := len(b) / 4 10 | for i := 0; i < full_blocks; i++ { 11 | for j := 0; j < 4; j++ { 12 | result[j] ^= b[4*i+j] 13 | } 14 | } 15 | if full_blocks*4 < len(b) { 16 | for j := 0; j < 4; j++ { 17 | if full_blocks*4+j < len(b) { 18 | result[j] ^= b[full_blocks*4+j] 19 | } else { 20 | result[j] ^= 0x55 21 | } 22 | } 23 | } 24 | return result 25 | } 26 | 27 | func CreateBytes(len byte) []byte { 28 | res := make([]byte, len) 29 | for i := (byte)(0); i < len; i++ { 30 | res[i] = i 31 | } 32 | return res 33 | } 34 | -------------------------------------------------------------------------------- /_examples/gobytes/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from __future__ import print_function 6 | import gobytes, go 7 | 8 | a = bytes([0, 1, 2, 3]) 9 | b = gobytes.CreateBytes(10) 10 | print ("Python bytes:", a) 11 | print ("Go slice: ", b) 12 | 13 | print ("gobytes.HashBytes from Go bytes:", gobytes.HashBytes(b)) 14 | 15 | print("Python bytes to Go: ", go.Slice_byte.from_bytes(a)) 16 | print("Go bytes to Python: ", bytes(go.Slice_byte([3, 4, 5]))) 17 | 18 | print("OK") 19 | -------------------------------------------------------------------------------- /_examples/gopyerrors/gopyerrors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package gopyerrors contains functions that generate error 6 | // messages from gopy itself. 7 | package gopyerrors 8 | 9 | func NotErrorMany() (int, int) { 10 | return 0, 0 11 | } 12 | 13 | func TooMany() (int, int, string) { 14 | return 0, 1, "Hi" 15 | } 16 | 17 | func OK() (int, error) { 18 | return 0, nil 19 | } 20 | 21 | type Struct struct{} 22 | 23 | func (s *Struct) NotErrorMany() (int, string) { 24 | return 0, "Hi" 25 | } 26 | 27 | func (s *Struct) TooMany() (int, int, string) { 28 | return 0, 1, "Hi" 29 | } 30 | -------------------------------------------------------------------------------- /_examples/gopyerrors/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import gopyerrors 9 | 10 | # This is empty, its only purpose is to have a test that catches 11 | # errors generated by the gopy itself. 12 | 13 | print("OK") 14 | -------------------------------------------------------------------------------- /_examples/gopygc/gopygc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package gopygc tests the interaction between the python gc system and gopy 6 | package gopygc 7 | 8 | import "bytes" 9 | 10 | type StructA struct { 11 | A int 12 | } 13 | 14 | type MapA map[string]string 15 | 16 | type SliceA []string 17 | 18 | func StructValue() StructA { 19 | return StructA{ 20 | A: 42, 21 | } 22 | } 23 | 24 | func SliceScalarValue() []int { 25 | return []int{1, 2} 26 | } 27 | 28 | func SliceStructValue() []StructA { 29 | return []StructA{{1}, {2}} 30 | } 31 | 32 | func MapValue() map[int]int { 33 | return map[int]int{1: 2} 34 | } 35 | 36 | func MapValueStruct() map[int]StructA { 37 | return map[int]StructA{1: StructA{3}} 38 | } 39 | 40 | func ExternalType() *bytes.Buffer { 41 | return &bytes.Buffer{} 42 | } 43 | -------------------------------------------------------------------------------- /_examples/gopygc/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import gopygc 9 | import _gopygc 10 | 11 | print(_gopygc.NumHandles()) 12 | 13 | 14 | # test literals 15 | a = gopygc.StructA() 16 | b = gopygc.SliceA() 17 | c = gopygc.MapA() 18 | print(_gopygc.NumHandles()) 19 | del a 20 | del b 21 | del c 22 | 23 | print(_gopygc.NumHandles()) 24 | a = [gopygc.StructValue(), gopygc.StructValue(), gopygc.StructValue()] 25 | print(_gopygc.NumHandles()) # 3 26 | b = [gopygc.SliceScalarValue(), gopygc.SliceScalarValue()] 27 | print(_gopygc.NumHandles()) # 5 28 | c = gopygc.SliceStructValue() 29 | print(_gopygc.NumHandles()) # 6 30 | d = gopygc.MapValue() 31 | print(_gopygc.NumHandles()) # 7 32 | e = gopygc.MapValueStruct() 33 | print(_gopygc.NumHandles()) # 8 34 | 35 | del a 36 | print(_gopygc.NumHandles()) # 5 37 | del b 38 | print(_gopygc.NumHandles()) # 3 39 | del c 40 | print(_gopygc.NumHandles()) # 2 41 | del d 42 | print(_gopygc.NumHandles()) # 1 43 | del e 44 | print(_gopygc.NumHandles()) # 0 45 | 46 | e1 = gopygc.ExternalType() 47 | print(_gopygc.NumHandles()) # 1 48 | del e1 49 | print(_gopygc.NumHandles()) # 0 50 | 51 | # test reference counting 52 | f = gopygc.SliceStructValue() 53 | print(_gopygc.NumHandles()) # 1 54 | g = gopygc.StructA(handle=f.handle) 55 | print(_gopygc.NumHandles()) # 1 56 | del g 57 | print(_gopygc.NumHandles()) # 1 58 | del f 59 | print(_gopygc.NumHandles()) # 0 60 | 61 | 62 | print("OK") 63 | -------------------------------------------------------------------------------- /_examples/gostrings/strings.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package gostrings 6 | 7 | var S1 = "S1" 8 | 9 | func GetString() string { 10 | return "MyString" 11 | } 12 | -------------------------------------------------------------------------------- /_examples/gostrings/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import gostrings 9 | 10 | print("S1 = %s" % (gostrings.S1(),)) 11 | print("GetString() = %s" % (gostrings.GetString(),)) 12 | 13 | print("OK") 14 | -------------------------------------------------------------------------------- /_examples/hi/hi.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package hi exposes a few Go functions to be wrapped and used from Python. 6 | package hi 7 | 8 | import ( 9 | "fmt" 10 | 11 | "github.com/go-python/gopy/_examples/cpkg" 12 | "github.com/go-python/gopy/_examples/structs" 13 | ) 14 | 15 | const ( 16 | Version = "0.1" // Version of this package 17 | Universe = 42 // Universe is the fundamental constant of everything 18 | ) 19 | 20 | var ( 21 | Debug = false // Debug switches between debug and prod 22 | Anon = Person{Age: 1, Name: ""} // Anon is a default anonymous person 23 | IntSlice = []int{1, 2} // A slice of ints 24 | IntArray = [2]int{1, 2} // An array of ints 25 | ) 26 | 27 | // Hi prints hi from Go 28 | func Hi() { 29 | cpkg.Hi() 30 | } 31 | 32 | // Hello prints a greeting from Go 33 | func Hello(s string) { 34 | cpkg.Hello(s) 35 | } 36 | 37 | // Concat concatenates two strings together and returns the resulting string. 38 | func Concat(s1, s2 string) string { 39 | return s1 + s2 40 | } 41 | 42 | // LookupQuestion returns question for given answer. 43 | func LookupQuestion(n int) (string, error) { 44 | if n == 42 { 45 | return "Life, the Universe and Everything", nil 46 | } else { 47 | return "", fmt.Errorf("Wrong answer: %v != 42", n) 48 | } 49 | } 50 | 51 | // Add returns the sum of its arguments. 52 | func Add(i, j int) int { 53 | return i + j 54 | } 55 | 56 | // Person is a simple struct 57 | type Person struct { 58 | Name string 59 | Age int 60 | } 61 | 62 | // NewPerson creates a new Person value 63 | func NewPerson(name string, age int) *Person { 64 | return &Person{ 65 | Name: name, 66 | Age: age, 67 | } 68 | } 69 | 70 | // PersonAsIface creates a new person as a PersIface interface 71 | func PersonAsIface(name string, age int) PersIface { 72 | return &Person{ 73 | Name: name, 74 | Age: age, 75 | } 76 | } 77 | 78 | // NewPersonWithAge creates a new Person with a specific age 79 | func NewPersonWithAge(age int) *Person { 80 | return &Person{ 81 | Name: "stranger", 82 | Age: age, 83 | } 84 | } 85 | 86 | // NewActivePerson creates a new Person with a certain amount of work done. 87 | func NewActivePerson(h int) (*Person, error) { 88 | p := &Person{} 89 | err := p.Work(h) 90 | return p, err 91 | } 92 | 93 | func (p Person) String() string { 94 | return fmt.Sprintf("hi.Person{Name=%q, Age=%d}", p.Name, p.Age) 95 | } 96 | 97 | // Greet sends greetings 98 | func (p *Person) Greet() string { 99 | return p.greet() 100 | } 101 | 102 | // greet sends greetings 103 | func (p *Person) greet() string { 104 | return fmt.Sprintf("Hello, I am %s", p.Name) 105 | } 106 | 107 | // Work makes a Person go to work for h hours 108 | func (p *Person) Work(h int) error { 109 | cpkg.Printf("working...\n") 110 | if h > 7 { 111 | return fmt.Errorf("can't work for %d hours!", h) 112 | } 113 | cpkg.Printf("worked for %d hours\n", h) 114 | return nil 115 | } 116 | 117 | // Salary returns the expected gains after h hours of work 118 | func (p *Person) Salary(h int) (int, error) { 119 | if h > 7 { 120 | return 0, fmt.Errorf("can't work for %d hours!", h) 121 | } 122 | return h * 10, nil 123 | } 124 | 125 | func (p *Person) GetName() string { 126 | return p.Name 127 | } 128 | 129 | func (p *Person) GetAge() int { 130 | return p.Age 131 | } 132 | 133 | func (p *Person) SetName(n string) { 134 | p.Name = n 135 | } 136 | 137 | func (p *Person) SetAge(age int) { 138 | p.Age = age 139 | } 140 | 141 | func (p *Person) SetFmS2(s2 structs.S2) { 142 | p.Age = s2.Public 143 | } 144 | 145 | func (p *Person) SetFmS2Ptr(s2 *structs.S2) { 146 | p.Age = s2.Public 147 | } 148 | 149 | func (p *Person) ReturnS2Ptr() *structs.S2 { 150 | s2 := &structs.S2{Public: p.Age} 151 | return s2 152 | } 153 | 154 | // Couple is a pair of persons 155 | type Couple struct { 156 | P1 Person 157 | P2 Person 158 | } 159 | 160 | // NewCouple returns a new couple made of the p1 and p2 persons. 161 | func NewCouple(p1, p2 Person) Couple { 162 | return Couple{ 163 | P1: p1, 164 | P2: p2, 165 | } 166 | } 167 | 168 | func (c *Couple) String() string { 169 | return fmt.Sprintf("hi.Couple{P1=%v, P2=%v}", c.P1, c.P2) 170 | } 171 | 172 | // Float is a kind of float32 173 | type Float float32 174 | 175 | // Floats is a slice of floats 176 | type Floats []Float 177 | 178 | // Eval evals float64 179 | type Eval func(f float64) float64 180 | 181 | // PersIface is an interface into the person type. 182 | type PersIface interface { 183 | // GetName returns the name of the person 184 | GetName() string 185 | 186 | // GetAge returns the age of the person 187 | GetAge() int 188 | 189 | // SetName sets name 190 | SetName(n string) 191 | 192 | // SetAge sets age 193 | SetAge(age int) 194 | 195 | // Greet sends greetings 196 | Greet() string 197 | } 198 | -------------------------------------------------------------------------------- /_examples/hi/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from __future__ import print_function 6 | 7 | import hi 8 | 9 | # NOTE: the output from python2 is different from that of 3, and test 10 | # targets python3 so it appears to fail for 2. 11 | 12 | print("--- doc(hi)...") 13 | print(hi.__doc__) 14 | 15 | print("--- hi.Universe:", hi.Universe) 16 | print("--- hi.Version:", hi.Version) 17 | 18 | print("--- hi.Debug():",hi.Debug()) 19 | print("--- hi.Set_Debug(true)") 20 | hi.Set_Debug(True) 21 | print("--- hi.Debug():",hi.Debug()) 22 | print("--- hi.Set_Debug(false)") 23 | hi.Set_Debug(False) 24 | print("--- hi.Debug():",hi.Debug()) 25 | 26 | print("--- hi.Anon():",hi.Anon()) 27 | anon = hi.NewPerson('you',24) 28 | print("--- new anon:",anon) 29 | print("--- hi.Set_Anon(hi.NewPerson('you', 24))...") 30 | hi.Set_Anon(anon) 31 | print("--- hi.Anon():",hi.Anon()) 32 | 33 | print("--- doc(hi.Hi)...") 34 | print(hi.Hi.__doc__) 35 | 36 | print("--- hi.Hi()...") 37 | hi.Hi() 38 | 39 | print("--- doc(hi.Hello)...") 40 | print(hi.Hello.__doc__) 41 | 42 | print("--- hi.Hello('you')...") 43 | hi.Hello("you") 44 | 45 | print("--- doc(hi.Add)...") 46 | print(hi.Add.__doc__) 47 | 48 | print("--- hi.Add(1, 41)...") 49 | print(hi.Add(1,41)) 50 | 51 | print("--- hi.Concat('4', '2')...") 52 | print(hi.Concat("4","2")) 53 | 54 | print("--- hi.LookupQuestion(42)...") 55 | print(hi.LookupQuestion(42)) 56 | 57 | print("--- hi.LookupQuestion(12)...") 58 | try: 59 | hi.LookupQuestion(12) 60 | print("*ERROR* no exception raised!") 61 | except Exception as err: 62 | print("caught:", err) 63 | pass 64 | 65 | print("--- doc(hi.Person):") 66 | print(hi.Person.__doc__) 67 | 68 | print("--- p = hi.Person()...") 69 | p = hi.Person() 70 | print("--- p:", p) 71 | 72 | print("--- p.Name:", p.Name) 73 | print("--- p.Age:",p.Age) 74 | 75 | print("--- doc(hi.Greet):") 76 | print(p.Greet.__doc__) 77 | print("--- p.Greet()...") 78 | print(p.Greet()) 79 | 80 | print("--- p.String()...") 81 | print(p.String()) 82 | 83 | print("--- doc(p):") 84 | print(p.__doc__) 85 | 86 | print("--- p.Name = \"foo\"...") 87 | p.Name = "foo" 88 | 89 | print("--- p.Age = 42...") 90 | p.Age = 42 91 | 92 | print("--- p.String()...") 93 | print(p.String()) 94 | print("--- p.Age:", p.Age) 95 | print("--- p.Name:",p.Name) 96 | 97 | print("--- p.Work(2)...") 98 | p.Work(2) 99 | 100 | print("--- p.Work(24)...") 101 | try: 102 | p.Work(24) 103 | print("*ERROR* no exception raised!") 104 | except Exception as err: 105 | print("caught:", err) 106 | pass 107 | 108 | print("--- p.Salary(2):", p.Salary(2)) 109 | try: 110 | print("--- p.Salary(24):",p.Salary(24)) 111 | print("*ERROR* no exception raised!") 112 | except Exception as err: 113 | print("--- p.Salary(24): caught:", err) 114 | pass 115 | 116 | ## test ctor args 117 | print("--- Person.__init__") 118 | try: 119 | hi.Person(1) 120 | print("*ERROR* no exception raised!") 121 | except Exception as err: 122 | print("caught:", err, "| err-type:",type(err)) 123 | pass 124 | 125 | try: 126 | hi.Person("name","2") 127 | print("*ERROR* no exception raised!") 128 | except Exception as err: 129 | print("caught:", err, "| err-type:",type(err)) 130 | pass 131 | 132 | try: 133 | hi.Person("name",2,3) 134 | print("*ERROR* no exception raised!") 135 | except Exception as err: 136 | print("caught:", err, "| err-type:",type(err)) 137 | pass 138 | 139 | p = hi.Person("name") 140 | print(p) 141 | p = hi.Person("name", 42) 142 | print(p) 143 | p = hi.Person(Name="name", Age=42) 144 | print(p) 145 | p = hi.Person(Age=42, Name="name") 146 | print(p) 147 | 148 | ## test ctors 149 | print("--- hi.NewPerson('me', 666):", hi.NewPerson("me", 666)) 150 | print("--- hi.NewPersonWithAge(666):", hi.NewPersonWithAge(666)) 151 | print("--- hi.NewActivePerson(4):") 152 | p = hi.NewActivePerson(4) 153 | print(p) 154 | 155 | ## test Couple 156 | print("--- c = hi.Couple()...") 157 | c = hi.Couple() 158 | print(c) 159 | print("--- c.P1:", c.P1) 160 | c.P1 = hi.NewPerson("tom", 5) 161 | c.P2 = hi.NewPerson("bob", 2) 162 | print("--- c:", c) 163 | 164 | print("--- c = hi.NewCouple(tom, bob)...") 165 | c = hi.NewCouple(hi.NewPerson("tom", 50), hi.NewPerson("bob", 41)) 166 | print(c) 167 | c.P1.Name = "mom" 168 | c.P2.Age = 51 169 | print(c) 170 | 171 | ## test Couple.__init__ 172 | print("--- Couple.__init__") 173 | # Note: pybindgen does not automatically support varargs, so in general 174 | # all python calls need to provide the full Go signature of args. 175 | #c = hi.Couple(hi.Person("p1", 42)) 176 | #print(c) 177 | c = hi.Couple(hi.Person("p1", 42), hi.Person("p2", 52)) 178 | print(c) 179 | c = hi.Couple(P1=hi.Person("p1", 42), P2=hi.Person("p2", 52)) 180 | print(c) 181 | c = hi.Couple(P2=hi.Person("p1", 42), P1=hi.Person("p2", 52)) 182 | print(c) 183 | 184 | try: 185 | hi.Couple(1) 186 | print("*ERROR* no exception raised!") 187 | except Exception as err: 188 | print("caught:", err, "| err-type:",type(err)) 189 | pass 190 | 191 | try: 192 | hi.Couple(1, 2) 193 | print("*ERROR* no exception raised!") 194 | except Exception as err: 195 | print("caught:", err, "| err-type:",type(err)) 196 | pass 197 | 198 | try: 199 | hi.Couple(P2=1) 200 | print("*ERROR* no exception raised!") 201 | except Exception as err: 202 | print("caught:", err, "| err-type:",type(err)) 203 | pass 204 | 205 | ### test gc 206 | print("--- testing GC...") 207 | NMAX = 100000 208 | objs = [] 209 | for i in range(NMAX): 210 | p1 = hi.NewPerson("p1-%d" % i, i) 211 | p2 = hi.NewPerson("p2-%d" % i, i) 212 | objs.append(hi.NewCouple(p1,p2)) 213 | pass 214 | print("--- len(objs):",len(objs)) 215 | vs = [] 216 | for i,o in enumerate(objs): 217 | v = "%d: %s" % (i, o) 218 | vs.append(v) 219 | pass 220 | print("--- len(vs):",len(vs)) 221 | del objs 222 | print("--- testing GC... [ok]") 223 | 224 | print("--- testing array...") 225 | arr = hi.IntArray() 226 | print("arr:",arr) 227 | print("len(arr):",len(arr)) 228 | print("arr[0]:",arr[0]) 229 | print("arr[1]:",arr[1]) 230 | try: 231 | print("arr[2]:", arr[2]) 232 | print("*ERROR* no exception raised!") 233 | except Exception as err: 234 | print("arr[2]: caught:",err) 235 | pass 236 | arr[1] = 42 237 | print("arr:",arr) 238 | print("len(arr):",len(arr)) 239 | try: 240 | print("mem(arr):",len(memoryview(arr))) 241 | except Exception as err: 242 | print("mem(arr): caught:",err) 243 | pass 244 | 245 | print("--- testing slice...") 246 | s = hi.IntSlice() 247 | print("slice:",s) 248 | print("len(slice):",len(s)) 249 | print("slice[0]:",s[0]) 250 | print("slice[1]:",s[1]) 251 | try: 252 | print("slice[2]:", s[2]) 253 | print("*ERROR* no exception raised!") 254 | except Exception as err: 255 | print("slice[2]: caught:",err) 256 | pass 257 | s[1] = 42 258 | print("slice:",s) 259 | print("slice repr:",s.__repr__()) 260 | print("len(slice):",len(s)) 261 | try: 262 | print("mem(slice):",len(memoryview(s))) 263 | except Exception as err: 264 | print("mem(slice): caught:",err) 265 | pass 266 | 267 | print("OK") 268 | -------------------------------------------------------------------------------- /_examples/iface/iface.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package iface tests various aspects of interfaces. 6 | package iface 7 | 8 | import ( 9 | "github.com/go-python/gopy/_examples/cpkg" 10 | ) 11 | 12 | // Iface has a single F() method 13 | type Iface interface { 14 | F() 15 | } 16 | 17 | // T implements Iface 18 | type T struct { 19 | FldI int 20 | FldS string 21 | } 22 | 23 | func (t *T) F() { 24 | cpkg.Printf("t.F [CALLED]\n") 25 | } 26 | 27 | // CallIface calls F() on v 28 | func CallIface(v Iface) { 29 | cpkg.Printf("iface.CallIface...\n") 30 | v.F() 31 | cpkg.Printf("iface.CallIface... [DONE]\n") 32 | } 33 | 34 | // by default, interface{} is converted to string (most universal type) 35 | func IfaceString(str interface{}) { 36 | cpkg.Printf("iface as string: %v\n", str) 37 | } 38 | 39 | // gopy:interface=handle 40 | // this magic directive says, treat the interface arg as a handle 41 | func IfaceHandle(ifc interface{}) { 42 | cpkg.Printf("iface as handle: %v\n", ifc) 43 | } 44 | -------------------------------------------------------------------------------- /_examples/iface/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import iface, go 9 | 10 | ### test docs 11 | print("doc(iface): %r" % (iface.__doc__,)) 12 | 13 | print("t = iface.T()") 14 | t = iface.T() 15 | print("t.F()") 16 | t.F() 17 | 18 | print("iface.CallIface(t)") 19 | iface.CallIface(t) 20 | 21 | print('iface.IfaceString("test string"') 22 | iface.IfaceString("test string") 23 | 24 | print('iface.IfaceString(str(42))') 25 | iface.IfaceString(str(42)) 26 | 27 | print('iface.IfaceHandle(t)') 28 | iface.IfaceHandle(t) 29 | 30 | print('iface.IfaceHandle(go.nil)') 31 | iface.IfaceHandle(go.nil) 32 | 33 | print("OK") 34 | -------------------------------------------------------------------------------- /_examples/lot/lot.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Reference from: https://github.com/initialed85/golang_python_binding_research 6 | 7 | package lot 8 | 9 | type Value struct { 10 | SomeString string 11 | SomeInt int64 12 | SomeFloat float64 13 | SomeBool bool 14 | SomeListOfStrings []string 15 | SomeListOfInts []int64 16 | SomeListOfFloats []float64 17 | SomeListOfBools []bool 18 | } 19 | 20 | // New returns a struct with exported fields of different types 21 | func New() Value { 22 | return Value{ 23 | "some string", 24 | 1337, 25 | 1337.1337, 26 | true, 27 | []string{"some", "list", "of", "strings"}, 28 | []int64{6, 2, 9, 1}, 29 | []float64{6.6, 2.2, 9.9, 1.1}, 30 | []bool{true, false, true, false}, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /_examples/lot/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | import lot 5 | 6 | l = lot.New() 7 | print('l.SomeString : {}'.format(l.SomeString)) 8 | print('l.SomeInt : {}'.format(l.SomeInt)) 9 | print('l.SomeFloat : {}'.format(l.SomeFloat)) 10 | print('l.SomeBool : {}'.format(l.SomeBool)) 11 | print('l.SomeListOfStrings: {}'.format(l.SomeListOfStrings)) 12 | print('l.SomeListOfInts: {}'.format(l.SomeListOfInts)) 13 | print('l.SomeListOfFloats: {}'.format(l.SomeListOfFloats)) 14 | print('l.SomeListOfBools: {}'.format(l.SomeListOfBools)) 15 | 16 | print("OK") 17 | -------------------------------------------------------------------------------- /_examples/maps/maps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package maps 6 | 7 | import ( 8 | "sort" 9 | ) 10 | 11 | func Sum(t map[int]float64) float64 { 12 | sum := 0.0 13 | for _, v := range t { 14 | sum += v 15 | } 16 | 17 | return sum 18 | } 19 | 20 | func New() map[int]float64 { 21 | return map[int]float64{ 22 | 1: 3.0, 23 | 2: 5.0, 24 | } 25 | } 26 | 27 | func Keys(t map[int]float64) []int { 28 | var keys []int 29 | for k, _ := range t { 30 | keys = append(keys, k) 31 | } 32 | 33 | sort.Ints(keys) 34 | return keys 35 | } 36 | 37 | func Values(t map[int]float64) []float64 { 38 | var values []float64 39 | for _, v := range t { 40 | values = append(values, v) 41 | } 42 | 43 | sort.Float64s(values) 44 | return values 45 | } 46 | -------------------------------------------------------------------------------- /_examples/maps/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from __future__ import print_function 6 | import maps 7 | 8 | a = maps.New() 9 | b = {1: 3.0, 2: 5.0} 10 | 11 | # set to true to test all the builtin map functionality 12 | # due to the random nature of map access in Go, this will 13 | # cause go test to fail randomly so it is off by default 14 | # but should be re-enabled when anything significant is 15 | # changed! 16 | testall = False 17 | 18 | if testall: 19 | print('map a:', a) 20 | print('map a repr:', a.__repr__()) 21 | 22 | print('map a keys:', a.keys()) 23 | print('map a values:', a.values()) 24 | print('map a items:', a.items()) 25 | print('map a iter') 26 | for k,v in a: 27 | print("key:", k, "value:", v) 28 | 29 | print('map a[1]:', a[1]) 30 | print('map a[2]:', a[2]) 31 | print('2 in map:', 2 in a) 32 | print('3 in map:', 3 in a) 33 | 34 | # TODO: not sure why python2 doesn't just catch this error, but it doesn't seem to.. 35 | # try: 36 | # v = a[4] 37 | # except Exception as err: 38 | # print("caught error: %s" % (err,)) 39 | # pass 40 | 41 | 42 | print('maps.Sum from Go map:', maps.Sum(a)) 43 | 44 | print('map b:', b) 45 | 46 | print('maps.Sum from Python dictionary:', maps.Sum(maps.Map_int_float64(b))) 47 | print('maps.Keys from Go map:', maps.Keys(a)) 48 | print('maps.Values from Go map:', maps.Values(a)) 49 | print('maps.Keys from Python dictionary:', maps.Keys(maps.Map_int_float64(b))) 50 | print('maps.Values from Python dictionary:', maps.Values(maps.Map_int_float64(b))) 51 | 52 | del a[1] 53 | print('deleted 1 from a:', a) 54 | 55 | print("OK") 56 | -------------------------------------------------------------------------------- /_examples/named/named.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package named tests various aspects of named types. 6 | package named 7 | 8 | // TODO: not dealing with named basic types -- not very intuitive to use 9 | // in python, as they have to become classes. instead anything like this 10 | // is converted to its basic type when used as an arg / return value 11 | // so you can use them in other methods, but their special type methods 12 | // are not exported 13 | 14 | // type Float float32 15 | // 16 | // // Value returns a float32 value 17 | // func (f Float) Value() float32 { return float32(f) } 18 | // 19 | // type X float32 20 | // type XX X 21 | // type XXX XX 22 | // type XXXX XXX 23 | // 24 | // // Value returns a float32 value 25 | // func (x X) Value() float32 { return float32(x) } 26 | // 27 | // // Value returns a float32 value 28 | // func (x XX) Value() float32 { return float32(x) } 29 | // 30 | // // Value returns a float32 value 31 | // func (x XXX) Value() float32 { return float32(x) } 32 | // 33 | // // Value returns a float32 value 34 | // func (x XXXX) Value() float32 { return float32(x) } 35 | // 36 | // type Str string 37 | 38 | // Value returns a string value 39 | // func (s Str) Value() string { return string(s) } 40 | 41 | type Slice []float64 42 | 43 | func (s Slice) At(i int) float64 { return s[i] } 44 | 45 | type Array [2]float64 46 | 47 | func (a Array) At(i int) float64 { return a[i] } 48 | 49 | // type T int 50 | // 51 | // func (t T) PublicMethod() {} 52 | //func (t T) privateMethod() {} 53 | -------------------------------------------------------------------------------- /_examples/named/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | import sys 8 | 9 | _PY3 = sys.version_info[0] == 3 10 | if _PY3: 11 | xrange = range 12 | pass 13 | 14 | import named 15 | 16 | ### test docs 17 | print("doc(named): %s" % repr(named.__doc__).lstrip('u')) 18 | # print("doc(named.Float): %s" % repr(named.Float.__doc__).lstrip('u')) 19 | # print("doc(named.Float.Value): %s" % repr(named.Float.Value.__doc__).lstrip('u')) 20 | # 21 | # print("v = named.Float()") 22 | # v = named.Float() 23 | # print("v = %s" % (v,)) 24 | # print("v.Value() = %s" % (v.Value(),)) 25 | # 26 | # print("x = named.X()") 27 | # x = named.X() 28 | # print("x = %s" % (x,)) 29 | # print("x.Value() = %s" % (x.Value(),)) 30 | # 31 | # print("x = named.XX()") 32 | # x = named.XX() 33 | # print("x = %s" % (x,)) 34 | # print("x.Value() = %s" % (x.Value(),)) 35 | # 36 | # print("x = named.XXX()") 37 | # x = named.XXX() 38 | # print("x = %s" % (x,)) 39 | # print("x.Value() = %s" % (x.Value(),)) 40 | # 41 | # print("x = named.XXXX()") 42 | # x = named.XXXX() 43 | # print("x = %s" % (x,)) 44 | # print("x.Value() = %s" % (x.Value(),)) 45 | # 46 | # ### test ctors 47 | # 48 | # print("v = named.Float(42)") 49 | # v = named.Float(42) 50 | # print("v = %s" % (v,)) 51 | # print("v.Value() = %s" % (v.Value(),)) 52 | # 53 | # print("v = named.Float(42.0)") 54 | # v = named.Float(42.0) 55 | # print("v = %s" % (v,)) 56 | # print("v.Value() = %s" % (v.Value(),)) 57 | # 58 | # print("x = named.X(42)") 59 | # x = named.X(42) 60 | # print("x = %s" % (x,)) 61 | # print("x.Value() = %s" % (x.Value(),)) 62 | # 63 | # print("x = named.XX(42)") 64 | # x = named.XX(42) 65 | # print("x = %s" % (x,)) 66 | # print("x.Value() = %s" % (x.Value(),)) 67 | # 68 | # print("x = named.XXX(42)") 69 | # x = named.XXX(42) 70 | # print("x = %s" % (x,)) 71 | # print("x.Value() = %s" % (x.Value(),)) 72 | # 73 | # print("x = named.XXXX(42)") 74 | # x = named.XXXX(42) 75 | # print("x = %s" % (x,)) 76 | # print("x.Value() = %s" % (x.Value(),)) 77 | # 78 | # print("x = named.XXXX(42.0)") 79 | # x = named.XXXX(42.0) 80 | # print("x = %s" % (x,)) 81 | # print("x.Value() = %s" % (x.Value(),)) 82 | # 83 | # print("s = named.Str()") 84 | # s = named.Str() 85 | # print("s = %s" % (s,)) 86 | # print("s.Value() = %s" % repr(s.Value()).lstrip('u')) 87 | # 88 | # print("s = named.Str('string')") 89 | # s = named.Str("string") 90 | # print("s = %s" % (s,)) 91 | # print("s.Value() = %s" % repr(s.Value()).lstrip('u')) 92 | 93 | # note: cannot construct arrays from python -- too risky wrt len etc -- use slices 94 | 95 | # print("arr = named.Array()") 96 | # arr = named.Array() 97 | # print("arr = %s" % (arr,)) 98 | 99 | # print("arr = named.Array([1,2])") 100 | # arr = named.Array([1,2]) 101 | # print("arr = %s" % (arr,)) 102 | # 103 | # try: 104 | # print("arr = named.Array(range(10))") 105 | # arr = named.Array(range(10)) 106 | # print("arr = %s" % (arr,)) 107 | # except Exception as err: 108 | # print("caught: %s" % (str(err),)) 109 | # pass 110 | 111 | # print("arr = named.Array(xrange(2))") 112 | # arr = named.Array(xrange(2)) 113 | # print("arr = %s" % (arr,)) 114 | # 115 | print("s = named.Slice()") 116 | s = named.Slice() 117 | print("s = %s" % (s,)) 118 | 119 | print("s = named.Slice([1,2])") 120 | s = named.Slice([1,2]) 121 | print("s = %s" % (s,)) 122 | 123 | print("s = named.Slice(range(10))") 124 | s = named.Slice(range(10)) 125 | print("s = %s" % (s,)) 126 | 127 | print("s = named.Slice(xrange(10))") 128 | s = named.Slice(xrange(10)) 129 | print("s = %s" % (s,)) 130 | 131 | print("OK") 132 | -------------------------------------------------------------------------------- /_examples/osfile/osfile.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package osfile 6 | 7 | import ( 8 | "io" 9 | "log" 10 | "os" 11 | ) 12 | 13 | // note: the real test is to access os.File, io.Writer etc directly 14 | // these funcs are just dummies to have something here to compile.. 15 | 16 | func OpenFile(fname string) *os.File { 17 | f, err := os.Create(fname) 18 | if err != nil { 19 | log.Println(err) 20 | return nil 21 | } 22 | return f 23 | } 24 | 25 | func WriteToFile(w io.Writer, str string) { 26 | _, err := w.Write([]byte(str)) 27 | if err != nil { 28 | log.Println(err) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /_examples/osfile/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | # note: this doesn't work without actually doing "make install" in pkg build dir 9 | # and furthermore, os is also used in python, so probably not very practical 10 | # to include it in the first place -- need to use a wrapper solution. 11 | 12 | from osfile import osfile, os, go 13 | 14 | try: 15 | f = os.Create("testfile.test") 16 | except Exception as err: 17 | print("os.Create got an error: %s" % (err,)) 18 | pass 19 | 20 | 21 | try: 22 | f.Write("this is a test of python writing to a Go-opened file\n") 23 | except Exception as err: 24 | print("file.Write got an error: %s" % (err,)) 25 | pass 26 | 27 | f.Close() 28 | 29 | try: 30 | os.Remove("testfile.test") 31 | except Exception as err: 32 | print("os.Remove got an error: %s" % (err,)) 33 | pass 34 | 35 | 36 | -------------------------------------------------------------------------------- /_examples/package/mypkg/mypkg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // mypkg is a nested package. 6 | package mypkg 7 | 8 | func SayHello() string { 9 | return "Hello" 10 | } 11 | -------------------------------------------------------------------------------- /_examples/package/mypkg/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import absolute_import, print_function 7 | 8 | import mypkg.mypkg 9 | 10 | print("mypkg.mypkg.SayHello()...") 11 | print(mypkg.mypkg.SayHello()) 12 | print("OK") 13 | -------------------------------------------------------------------------------- /_examples/pkgconflict/html.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pkgconflict 6 | 7 | import ( 8 | "html/template" 9 | "os" 10 | ) 11 | 12 | func HtmlTempl(nm string) *template.Template { 13 | sweaters := Inventory{"hyperlinks", 42} 14 | t, err := template.New(nm).Parse("{{.Count}} items are made of {{.Material}}\n") 15 | if err != nil { 16 | panic(err) 17 | } 18 | err = t.Execute(os.Stdout, sweaters) 19 | if err != nil { 20 | panic(err) 21 | } 22 | return t 23 | } 24 | -------------------------------------------------------------------------------- /_examples/pkgconflict/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import pkgconflict 9 | 10 | txt = pkgconflict.TextTempl("text") 11 | # note: print is for confirming correct return type, but has addr so not good for testing 12 | # print("txt = %s" % txt) 13 | 14 | htm = pkgconflict.HtmlTempl("html") 15 | # print("htm = %s" % htm) 16 | 17 | htm2 = pkgconflict.HtmlTemplSame("html2") 18 | # print("htm2 = %s" % htm2) 19 | 20 | -------------------------------------------------------------------------------- /_examples/pkgconflict/text.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pkgconflict 6 | 7 | import ( 8 | htemplate "html/template" 9 | "os" 10 | "text/template" 11 | ) 12 | 13 | type Inventory struct { 14 | Material string 15 | Count uint 16 | } 17 | 18 | func TextTempl(nm string) *template.Template { 19 | sweaters := Inventory{"wool", 17} 20 | t, err := template.New(nm).Parse("{{.Count}} items are made of {{.Material}}\n") 21 | if err != nil { 22 | panic(err) 23 | } 24 | err = t.Execute(os.Stdout, sweaters) 25 | if err != nil { 26 | panic(err) 27 | } 28 | return t 29 | } 30 | 31 | func HtmlTemplSame(nm string) *htemplate.Template { 32 | sweaters := Inventory{"fuzz", 2} 33 | t, err := htemplate.New(nm).Parse("{{.Count}} items are made of {{.Material}}\n") 34 | if err != nil { 35 | panic(err) 36 | } 37 | err = t.Execute(os.Stdout, sweaters) 38 | if err != nil { 39 | panic(err) 40 | } 41 | return t 42 | } 43 | -------------------------------------------------------------------------------- /_examples/pointers/pointers.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pointers 6 | 7 | //type SPtr *S 8 | 9 | type S struct { 10 | Value int 11 | } 12 | 13 | // Inc increments the value of s 14 | func Inc(s *S) { 15 | s.Value++ 16 | } 17 | 18 | // note: pointers to basic types are not supported -- would 19 | // require a handle -- could to, but probably not worth it.. 20 | type MyInt int 21 | 22 | // IncInt increments an integer 23 | func IncMyInt(i *MyInt) { 24 | (*i)++ 25 | } 26 | 27 | // IncInt increments an integer 28 | func IncInt(i *int) { 29 | (*i)++ 30 | } 31 | -------------------------------------------------------------------------------- /_examples/pointers/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import pointers 9 | 10 | print("s = pointers.S(2)") 11 | s = pointers.S(Value=2) 12 | print("s = %s" % (s,)) 13 | print("s.Value = %s" % (s.Value,)) 14 | 15 | # note: pointers to basic types (int) not supported - would 16 | # require a handle -- could to, but probably not worth it.. 17 | #print("pointers.Inc(s)") 18 | #print("s.Value = %s" % (s.Value,)) 19 | 20 | print("OK") 21 | -------------------------------------------------------------------------------- /_examples/pyerrors/pyerrors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package pyerrors holds functions returning an error. 6 | package pyerrors 7 | 8 | import ( 9 | "errors" 10 | "fmt" 11 | ) 12 | 13 | // Div is a function for detecting errors. 14 | func Div(i, j int) (int, error) { 15 | if j == 0 { 16 | return 0, errors.New("Divide by zero.") 17 | } 18 | return i / j, nil 19 | } 20 | 21 | type Stringer fmt.Stringer 22 | type MyString string 23 | 24 | func (t MyString) String() string { return string(t) } 25 | 26 | // NewMyString converts a string to a custom MyString type. 27 | // It is an error to pass an empty string value. 28 | func NewMyString(val string) (stringer Stringer, err error) { 29 | if val == "" { 30 | err = errors.New("Empty string value.") 31 | return 32 | } 33 | return MyString(val), nil 34 | } 35 | -------------------------------------------------------------------------------- /_examples/pyerrors/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import pyerrors 9 | 10 | def div(a, b): 11 | try: 12 | r = pyerrors.Div(a, b) 13 | print("pyerrors.Div(%d, %d) = %d"% (a, b, r)) 14 | except Exception as e: 15 | print(e) 16 | 17 | 18 | def new_mystring(s): 19 | try: 20 | ms = pyerrors.NewMyString(s) 21 | print('pyerrors.NewMyString("%s") = "%s"'% (s, ms.String())) 22 | except Exception as e: 23 | print(e) 24 | 25 | 26 | div(5,0) # error 27 | div(5,2) 28 | 29 | new_mystring("") # error 30 | new_mystring("hello") 31 | 32 | print("OK") 33 | -------------------------------------------------------------------------------- /_examples/rename/rename.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package rename tests changing the names of methods and functions 6 | package rename 7 | 8 | // gopy:name say_hi_fn 9 | // Comment follows the tag and function should be renamed 10 | // to say_hi_fn 11 | func SayHi() string { 12 | return "hi" 13 | } 14 | 15 | // I should be renamed to auto_renamed_func, when generated 16 | // with -rename flag 17 | func AutoRenamedFunc() { 18 | 19 | } 20 | 21 | // MyStruct has two fields 22 | type MyStruct struct { 23 | // I should be renamed to auto_renamed_property 24 | // when generated with -rename flag 25 | AutoRenamedProperty string 26 | 27 | // I should be renamed to custom_name with the custom option 28 | AutoRenamedProperty2 string `gopy:"custom_name"` 29 | } 30 | 31 | // A method that says something 32 | // gopy:name say_something 33 | func (s *MyStruct) SaySomethingFunc() (something string) { 34 | return "something" 35 | } 36 | 37 | // I should be renamed to auto_renamed_meth, when generated 38 | // with -rename flag 39 | func (s *MyStruct) AutoRenamedMeth() { 40 | 41 | } 42 | -------------------------------------------------------------------------------- /_examples/rename/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import rename 9 | 10 | print("say_hi_fn():", rename.say_hi_fn()) 11 | print("MyStruct().say_something():", rename.MyStruct().say_something()) 12 | 13 | # Just make sure the symbols exist 14 | rename.auto_renamed_func() 15 | struct = rename.MyStruct() 16 | struct.auto_renamed_meth() 17 | _ = struct.auto_renamed_property 18 | struct.auto_renamed_property = "foo" 19 | _ = struct.custom_name 20 | struct.custom_name = "foo" 21 | 22 | print("MyStruct.auto_renamed_property.__doc__:", rename.MyStruct.auto_renamed_property.__doc__.strip()) 23 | print("MyStruct.custom_name.__doc__:", rename.MyStruct.custom_name.__doc__.strip()) 24 | 25 | print("OK") 26 | -------------------------------------------------------------------------------- /_examples/seqs/seqs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package seqs tests various aspects of sequence types. 6 | package seqs 7 | 8 | type Slice []float64 9 | 10 | func (s Slice) At(i int) float64 { return s[i] } 11 | 12 | type Array [10]float64 13 | 14 | func (a Array) At(i int) float64 { return a[i] } 15 | -------------------------------------------------------------------------------- /_examples/seqs/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import sys 9 | _PY3 = sys.version_info[0] == 3 10 | if _PY3: 11 | xrange = range 12 | 13 | import seqs 14 | 15 | ### test docs 16 | print("doc(seqs): %s" % repr(seqs.__doc__).lstrip('u')) 17 | 18 | # note: arrays not settable from python -- use slices instead 19 | 20 | # print("arr = seqs.Array(xrange(2))") 21 | # arr = seqs.Array(xrange(2)) 22 | # print("arr = %s" % (arr,)) 23 | # 24 | print("s = seqs.Slice()") 25 | s = seqs.Slice() 26 | print("s = %s" % (s,)) 27 | 28 | print("s = seqs.Slice([1,2])") 29 | s = seqs.Slice([1,2]) 30 | print("s = %s" % (s,)) 31 | 32 | print("s = seqs.Slice(range(10))") 33 | s = seqs.Slice(range(10)) 34 | print("s = %s" % (s,)) 35 | 36 | print("s = seqs.Slice(xrange(10))") 37 | s = seqs.Slice(xrange(10)) 38 | print("s = %s" % (s,)) 39 | 40 | print("s = seqs.Slice()") 41 | s = seqs.Slice() 42 | print("s = %s" % (s,)) 43 | print("s += [1,2]") 44 | s += [1,2] 45 | print("s = %s" % (s,)) 46 | print("s += [10,20]") 47 | s += [10,20] 48 | print("s = %s" % (s,)) 49 | 50 | print("OK") 51 | -------------------------------------------------------------------------------- /_examples/simple/simple.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // simple is a simple package. 6 | package simple 7 | 8 | // Func is a simple func 9 | func Func() {} 10 | 11 | // Add is a (less) simple func 12 | func Add(i, j int) int { 13 | return i + j 14 | } 15 | 16 | func Bool(b bool) bool { 17 | return b 18 | } 19 | 20 | func Comp64Add(i, j complex64) complex64 { 21 | return i + j 22 | } 23 | 24 | func Comp128Add(i, j complex128) complex128 { 25 | return i + j 26 | } 27 | -------------------------------------------------------------------------------- /_examples/simple/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import simple as pkg 9 | 10 | print("doc(pkg):\n%s" % repr(pkg.__doc__).lstrip('u')) 11 | print("pkg.Func()...") 12 | pkg.Func() 13 | print("fct = pkg.Func...") 14 | fct = pkg.Func 15 | print("fct()...") 16 | fct() 17 | 18 | print("pkg.Add(1,2)= %s" % (pkg.Add(1,2),)) 19 | print("pkg.Bool(True)= %s" % pkg.Bool(True)) 20 | print("pkg.Bool(False)= %s" % pkg.Bool(False)) 21 | a = 3+4j 22 | b = 2+5j 23 | print("pkg.Comp64Add(%s, %s) = %s" % (a, b, pkg.Comp128Add(a, b))) 24 | print("pkg.Comp128Add(%s, %s) = %s" % (a, b, pkg.Comp128Add(a, b))) 25 | 26 | print("OK") 27 | -------------------------------------------------------------------------------- /_examples/sliceptr/sliceptr.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // package sliceptr tests support for pointer-to-slice arguments 6 | // useful for getting bulk data from Go to Python 7 | 8 | package sliceptr 9 | 10 | import "strconv" 11 | 12 | type StrVector []string 13 | type IntVector []int 14 | 15 | func Fill(iv *IntVector) { 16 | *iv = IntVector{1, 2, 3} 17 | } 18 | 19 | func Append(iv *IntVector) { 20 | *iv = append(*iv, 4) 21 | } 22 | 23 | func Convert(iv *IntVector, sv *StrVector) { 24 | for _, v := range *iv { 25 | *sv = append(*sv, strconv.Itoa(v)) 26 | } 27 | } 28 | 29 | // compiles, but dies with SIGSEGV 30 | //func (iv *IntVector) Convert(sv *StrVector) { 31 | // for _, v := range *iv { 32 | // *sv = append(*sv, strconv.Itoa(v)) 33 | // } 34 | //} 35 | -------------------------------------------------------------------------------- /_examples/sliceptr/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import sliceptr 9 | 10 | i = sliceptr.IntVector() 11 | 12 | sliceptr.Fill(i) 13 | print(i) 14 | sliceptr.Append(i) 15 | print(i) 16 | s = sliceptr.StrVector() 17 | sliceptr.Convert(i, s) 18 | print(s) 19 | 20 | print("OK") 21 | -------------------------------------------------------------------------------- /_examples/slices/slices.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package slices 6 | 7 | import ( 8 | "fmt" 9 | "math/cmplx" 10 | ) 11 | 12 | func IntSum(s []int) int { 13 | sum := 0 14 | for _, value := range s { 15 | sum += value 16 | } 17 | return sum 18 | } 19 | 20 | func CreateSlice() []int { 21 | return []int{1, 2, 3, 4} 22 | } 23 | 24 | type SliceUint8 []uint8 25 | type SliceUint16 []uint16 26 | type SliceUint32 []uint32 27 | type SliceUint64 []uint64 28 | 29 | type SliceInt8 []int8 30 | type SliceInt16 []int16 31 | type SliceInt32 []int32 32 | type SliceInt64 []int64 33 | 34 | type SliceComplex []complex128 35 | 36 | type SliceIface []interface{} 37 | 38 | type S struct { 39 | Name string 40 | } 41 | 42 | func CreateSSlice() []*S { 43 | return []*S{&S{"S0"}, &S{"S1"}, &S{"S2"}} 44 | } 45 | 46 | func PrintSSlice(ss []*S) { 47 | for i, s := range ss { 48 | fmt.Printf("%d: %v\n", i, s.Name) 49 | } 50 | } 51 | 52 | func PrintS(s *S) { 53 | fmt.Printf("%v\n", s.Name) 54 | } 55 | 56 | func CmplxSqrt(arr SliceComplex) SliceComplex { 57 | res := make([]complex128, len(arr)) 58 | for i, el := range arr { 59 | res[i] = cmplx.Sqrt(el) 60 | } 61 | return res 62 | } 63 | 64 | func GetEmptyMatrix(xSize int, ySize int) [][]bool { 65 | result := [][]bool{} 66 | 67 | for i := 0; i < xSize; i++ { 68 | result = append(result, []bool{}) 69 | for j := 0; j < ySize; j++ { 70 | result[i] = append(result[i], false) 71 | } 72 | } 73 | 74 | return result 75 | } 76 | -------------------------------------------------------------------------------- /_examples/slices/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | from __future__ import print_function 6 | import math 7 | import random 8 | import slices, go 9 | 10 | a = [1,2,3,4] 11 | b = slices.CreateSlice() 12 | print ("Python list:", a) 13 | print ("Go slice: ", b) 14 | print ("slices.IntSum from Python list:", slices.IntSum(go.Slice_int(a))) 15 | print ("slices.IntSum from Go slice:", slices.IntSum(b)) 16 | 17 | su8 = slices.SliceUint8([1,2]) 18 | su16 = slices.SliceUint16([2,3]) 19 | su32 = slices.SliceUint32([3,4]) 20 | su64 = slices.SliceUint64([4,5]) 21 | print ("unsigned slice elements:", su8[0], su16[0], su32[0], su64[0]) 22 | 23 | si8 = slices.SliceInt8([-1,-2]) 24 | si16 = slices.SliceInt16([-2,-3]) 25 | si32 = slices.SliceInt32([-3,-4]) 26 | si64 = slices.SliceInt64([-4,-5]) 27 | print ("signed slice elements:", si8[0], si16[0], si32[0], si64[0]) 28 | 29 | ss = slices.CreateSSlice() 30 | print ("struct slice: ", ss) 31 | print ("struct slice[0]: ", ss[0]) 32 | print ("struct slice[1]: ", ss[1]) 33 | print ("struct slice[2].Name: ", ss[2].Name) 34 | 35 | slices.PrintSSlice(ss) 36 | 37 | slices.PrintS(ss[0]) 38 | slices.PrintS(ss[1]) 39 | 40 | cmplx = slices.SliceComplex([(random.random() + random.random() * 1j) for _ in range(16)]) 41 | sqrts = slices.CmplxSqrt(cmplx) 42 | for root, orig in zip(sqrts, cmplx): 43 | root_squared = root * root 44 | assert math.isclose(root_squared.real, orig.real) 45 | assert math.isclose(root_squared.imag, orig.imag) 46 | 47 | 48 | matrix = slices.GetEmptyMatrix(4,4) 49 | for i in range(4): 50 | for j in range(4): 51 | assert not matrix[i][j] 52 | print("[][]bool working as expected") 53 | 54 | print("OK") 55 | -------------------------------------------------------------------------------- /_examples/structs/structs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package structs 6 | 7 | import ( 8 | "strings" 9 | ) 10 | 11 | type S struct{} 12 | 13 | func (S) Init() {} 14 | func (S) Upper(s string) string { 15 | return strings.ToUpper(s) 16 | } 17 | 18 | func FuncTest(item S) {} 19 | 20 | func (this S) MethodTest(item S1) {} 21 | 22 | type S1 struct { 23 | private int 24 | } 25 | 26 | type S2 struct { 27 | Public int 28 | private int 29 | } 30 | 31 | type Dim int 32 | 33 | type S3 struct { 34 | X Dim 35 | Y Dim 36 | } 37 | -------------------------------------------------------------------------------- /_examples/structs/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | # py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import structs 9 | 10 | print("s = structs.S()") 11 | s = structs.S() 12 | print("s = %s" % (s,)) 13 | print("s.Init()") 14 | s.Init() 15 | print("s.Upper('boo')= %s" % repr(s.Upper("boo")).lstrip('u')) 16 | 17 | print("s1 = structs.S1()") 18 | s1 = structs.S1() 19 | print("s1 = %s" % (s1,)) 20 | 21 | try: 22 | s1 = structs.S1(1) 23 | except Exception as err: 24 | print("caught error: %s" % (err,)) 25 | pass 26 | 27 | try: 28 | s1 = structs.S1() 29 | print("s1.private = %s" % (s1.private,)) 30 | except Exception as err: 31 | print("caught error: %s" % (err,)) 32 | pass 33 | 34 | 35 | print("s2 = structs.S2()") 36 | s2 = structs.S2(1) 37 | print("s2 = %s" % (s2,)) 38 | 39 | try: 40 | s2 = structs.S2(1, 2) 41 | except Exception as err: 42 | print("caught error: %s" % (err,)) 43 | pass 44 | 45 | try: 46 | s2 = structs.S2(42) 47 | print("s2 = %s" % (s2,)) 48 | print("s2.Public = %s" % (s2.Public,)) 49 | print("s2.private = %s" % (s2.private,)) 50 | except Exception as err: 51 | print("caught error: %s" % (err,)) 52 | pass 53 | 54 | 55 | class S2Child(structs.S2): 56 | def __init__(self, a, b): 57 | super(S2Child, self).__init__(a) 58 | self.local = b 59 | 60 | def __str__(self): 61 | return ("S2Child{S2: %s, local: %d}" 62 | % (super(S2Child, self).__str__(), self.local)) 63 | 64 | 65 | try: 66 | s2child = S2Child(42, 123) 67 | print("s2child = %s" % (s2child,)) 68 | print("s2child.Public = %s" % (s2child.Public,)) 69 | print("s2child.local = %s" % (s2child.local,)) 70 | print("s2child.private = %s" % (s2child.private,)) 71 | except Exception as err: 72 | print("caught error: %s" % (err,)) 73 | pass 74 | 75 | try: 76 | val = structs.S3() 77 | val.X = 3 78 | val.Y = 4 79 | print("s3.X,Y = %d,%d" % (val.X, val.Y)) 80 | except Exception as err: 81 | print("caught error: %s" % (err,)) 82 | pass 83 | 84 | print("OK") 85 | -------------------------------------------------------------------------------- /_examples/unicode/encoding.go: -------------------------------------------------------------------------------- 1 | // Copyright 2018 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package encoding 6 | 7 | var gostring = "Go Unicode string 🐱" 8 | 9 | func HandleString(s string) string { 10 | return s 11 | } 12 | 13 | func GetString() string { 14 | return gostring 15 | } 16 | -------------------------------------------------------------------------------- /_examples/unicode/test.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2018 The go-python Authors. All rights reserved. 4 | # Use of this source code is governed by a BSD-style 5 | # license that can be found in the LICENSE file. 6 | 7 | ## py2/py3 compat 8 | from __future__ import print_function, unicode_literals 9 | 10 | import sys 11 | 12 | import encoding 13 | 14 | # There is no portable way of outputting Unicode in Python 2/3 -- we encode 15 | # them manually and sidestep the builtin encoding machinery 16 | try: 17 | binary_stdout = sys.stdout.buffer # Python 3 18 | except AttributeError: 19 | binary_stdout = sys.stdout # Python 2 20 | 21 | bytestr = b"Python byte string" 22 | unicodestr = u"Python Unicode string 🐱" 23 | 24 | # TODO: need conversion from bytestr to string -- not sure pybindgen can do it? 25 | #bytestr_ret = encoding.HandleString(bytestr) 26 | unicodestr_ret = encoding.HandleString(unicodestr) 27 | 28 | # binary_stdout.write(b"encoding.HandleString(bytestr) -> ") 29 | # binary_stdout.write(bytestr_ret.encode('UTF-8')) 30 | # binary_stdout.write(b'\n') 31 | binary_stdout.write(b"encoding.HandleString(unicodestr) -> ") 32 | binary_stdout.write(unicodestr_ret.encode('UTF-8')) 33 | binary_stdout.write(b'\n') 34 | 35 | gostring_ret = encoding.GetString() 36 | binary_stdout.write(b"encoding.GetString() -> ") 37 | binary_stdout.write(gostring_ret.encode("UTF-8")) 38 | binary_stdout.write(b"\n") 39 | 40 | print("OK") 41 | -------------------------------------------------------------------------------- /_examples/variadic/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | import variadic, go 5 | 6 | ############### Non Variadic ############## 7 | nonvarResult = variadic.NonVariFunc(1, go.Slice_int([2,3,4]),5) 8 | print("NonVariadic 1+[2+3+4]+5 = %d" % nonvarResult) 9 | 10 | ############### Variadic Over Int ############## 11 | varResult = variadic.VariFunc(1,2,3,4,5) 12 | print("Variadic 1+2+3+4+5 = %d" % varResult) 13 | 14 | ############### Variadic Over Struct ############## 15 | varStructResult = variadic.VariStructFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3)) 16 | print("Variadic Struct s(1)+s(2)+s(3) = %d" % varStructResult) 17 | 18 | ############### Variadic Over InterFace ############## 19 | varInterFaceResult = variadic.VariInterFaceFunc(variadic.NewIntStrUct(1), variadic.NewIntStrUct(2), variadic.NewIntStrUct(3)) 20 | print("Variadic InterFace i(1)+i(2)+i(3) = %d" % varInterFaceResult) 21 | 22 | ############### Final ############## 23 | if isinstance(varResult, int): 24 | print("Type OK") 25 | else: 26 | print("Type Not OK") 27 | -------------------------------------------------------------------------------- /_examples/variadic/variadic.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package variadic 6 | 7 | // ///////////// Non Variadic ////////////// 8 | func NonVariFunc(arg1 int, arg2 []int, arg3 int) int { 9 | total := arg1 10 | for _, num := range arg2 { 11 | total += num 12 | } 13 | total += arg3 14 | 15 | return total 16 | } 17 | 18 | // ///////////// Variadic Over Int ////////////// 19 | func VariFunc(vargs ...int) int { 20 | total := 0 21 | for _, num := range vargs { 22 | total += num 23 | } 24 | return total 25 | } 26 | 27 | // ///////////// Variadic Over Struct ////////////// 28 | type IntStrUct struct { 29 | p int 30 | } 31 | 32 | func NewIntStrUct(n int) IntStrUct { 33 | return IntStrUct{ 34 | p: n, 35 | } 36 | } 37 | 38 | func VariStructFunc(vargs ...IntStrUct) int { 39 | total := 0 40 | for _, inst := range vargs { 41 | total += inst.p 42 | } 43 | return total 44 | } 45 | 46 | // ///////////// Variadic Over Interface ////////////// 47 | type IntInterFace interface { 48 | Number() int 49 | } 50 | 51 | func (is *IntStrUct) Number() int { 52 | return is.p 53 | } 54 | 55 | func VariInterFaceFunc(vargs ...IntInterFace) int { 56 | total := 0 57 | for _, inst := range vargs { 58 | total += inst.Number() 59 | } 60 | return total 61 | } 62 | -------------------------------------------------------------------------------- /_examples/vars/test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ## py2/py3 compat 6 | from __future__ import print_function 7 | 8 | import vars 9 | 10 | print("doc(vars):\n%s" % repr(vars.__doc__).lstrip('u')) 11 | print("doc(vars.V1()):\n%s" % repr(vars.V1.__doc__).lstrip('u')) 12 | print("doc(vars.Set_V1()):\n%s" % repr(vars.Set_V1.__doc__).lstrip('u')) 13 | 14 | print("Initial values") 15 | print("v1 = %s" % vars.V1()) 16 | print("v2 = %s" % vars.V2()) 17 | print("v3 = %s" % vars.V3()) 18 | print("v4 = %s" % vars.V4()) 19 | print("v5 = %s" % vars.V5()) 20 | print("v6 = %s" % vars.V6()) 21 | print("v7 = %s" % vars.V7()) 22 | 23 | print("k1 = %s" % vars.Kind1()) 24 | print("k2 = %s" % vars.Kind2()) 25 | ## FIXME: unexported types not supported yet (issue #44) 26 | #print("k3 = %s" % vars.Kind3()) 27 | #print("k4 = %s" % vars.Kind4()) 28 | 29 | vars.Set_V1("test1") 30 | vars.Set_V2(90) 31 | vars.Set_V3(1111.1111) 32 | vars.Set_V4("test2") 33 | vars.Set_V5(50) 34 | vars.Set_V6(50) 35 | vars.Set_V7(1111.1111) 36 | 37 | vars.Set_Kind1(123) 38 | vars.Set_Kind2(456) 39 | print("New values") 40 | print("v1 = %s" % vars.V1()) 41 | print("v2 = %s" % vars.V2()) 42 | print("v3 = %s" % vars.V3()) 43 | print("v4 = %s" % vars.V4()) 44 | print("v5 = %s" % vars.V5()) 45 | print("v6 = %s" % vars.V6()) 46 | print("v7 = %s" % vars.V7()) 47 | 48 | print("k1 = %s" % vars.Kind1()) 49 | print("k2 = %s" % vars.Kind2()) 50 | 51 | print("vars.Doc() = %s" % repr(vars.Doc()).lstrip('u')) 52 | print("doc of vars.Doc = %s" % repr(vars.Doc.__doc__).lstrip('u')) 53 | print("doc of vars.Set_Doc = %s" % repr(vars.Set_Doc.__doc__).lstrip('u')) 54 | 55 | print("OK") 56 | -------------------------------------------------------------------------------- /_examples/vars/vars.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package vars 6 | 7 | var ( 8 | V1 = "v1" 9 | V2 = 42 10 | V3 = 666.666 11 | ) 12 | 13 | var ( 14 | V4 string = "c4" 15 | V5 int = 42 16 | V6 uint = 42 17 | V7 float64 = 666.666 18 | ) 19 | 20 | type Kind int 21 | 22 | var ( 23 | Kind1 Kind = 1 24 | Kind2 = 2 25 | ) 26 | 27 | // Doc is a top-level string with some documentation attached. 28 | var Doc = "A variable with some documentation" 29 | 30 | // FIXME: also use an unexported type 31 | // type kind int 32 | // var ( 33 | // Kind3 kind = 3 34 | // Kind4 = 4 35 | // ) 36 | -------------------------------------------------------------------------------- /_examples/wrapper/pywrapper/wrapper_code.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package pywrapper 6 | 7 | import ( 8 | "fmt" 9 | 10 | "github.com/go-python/gopy/_examples/wrapper" 11 | ) 12 | 13 | type WrapperStruct struct { 14 | data *wrapper.RealStruct 15 | } 16 | 17 | func Test() string { 18 | a := wrapper.PointerTest() 19 | fmt.Println("%t", a) 20 | return "Hello" 21 | } 22 | 23 | func (a WrapperStruct) Test() string { 24 | return fmt.Sprintf("%t", a.data) 25 | } 26 | -------------------------------------------------------------------------------- /_examples/wrapper/real_code.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package wrapper 6 | 7 | type RealStruct struct { 8 | pointers map[string]*RealStruct 9 | Channel chan int 10 | } 11 | 12 | func PointerTest() *RealStruct { 13 | return &RealStruct{} 14 | } 15 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | image: Previous Visual Studio 2019 2 | 3 | build: off 4 | 5 | clone_folder: c:\gopath\src\github.com\go-python\gopy 6 | 7 | cache: 8 | - '%LocalAppData%\\go-build' 9 | - '%LocalAppData%\\pip' 10 | 11 | branches: 12 | only: 13 | - master 14 | 15 | environment: 16 | GOPATH: C:\gopath 17 | GOROOT: C:\go121 18 | GOPY_APPVEYOR_CI: '1' 19 | GOTRACEBACK: 'crash' 20 | CPYTHON3DIR: "C:\\Python311-x64" 21 | PATH: '%GOPATH%\bin;%GOROOT%\bin;%CPYTHON3DIR%;%CPYTHON3DIR%\\Scripts;C:\msys64\mingw64\bin;C:\msys64\usr\bin\;%PATH%' 22 | 23 | stack: go 1.21 24 | 25 | build_script: 26 | - python --version 27 | - "%CPYTHON3DIR%\\python --version" 28 | - "%CPYTHON3DIR%\\python -m pip install --upgrade pip" 29 | - "%CPYTHON3DIR%\\python -m pip install cffi" 30 | - "%CPYTHON3DIR%\\python -m pip install pybindgen" 31 | - go version 32 | - go env 33 | - go get -v -t ./... 34 | - go install golang.org/x/tools/cmd/goimports@latest 35 | 36 | test_script: 37 | - go test ./... 38 | -------------------------------------------------------------------------------- /bind/bind.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "fmt" 11 | "io" 12 | "os" 13 | ) 14 | 15 | // BindCfg is a configuration used during binding generation 16 | type BindCfg struct { 17 | // output directory for bindings 18 | OutputDir string 19 | // name of output package (otherwise name of first package is used) 20 | Name string 21 | // code string to run in the go main() function in the cgo library 22 | Main string 23 | // the full command args as a string, without path to exe 24 | Cmd string 25 | // path to python interpreter 26 | VM string 27 | // package prefix used when generating python import statements 28 | PkgPrefix string 29 | // rename Go exported symbols to python PEP snake_case 30 | RenameCase bool 31 | } 32 | 33 | // ErrorList is a list of errors 34 | type ErrorList []error 35 | 36 | func (list *ErrorList) Add(err error) { 37 | if err == nil { 38 | return 39 | } 40 | *list = append(*list, err) 41 | } 42 | 43 | func (list *ErrorList) Error() error { 44 | buf := new(bytes.Buffer) 45 | for i, err := range *list { 46 | if i > 0 { 47 | buf.WriteRune('\n') 48 | } 49 | io.WriteString(buf, err.Error()) 50 | } 51 | return errors.New(buf.String()) 52 | } 53 | 54 | const ( 55 | doDebug = true 56 | ) 57 | 58 | func debugf(format string, args ...interface{}) (int, error) { 59 | if doDebug { 60 | return fmt.Fprintf(os.Stderr, format, args...) 61 | } 62 | return 0, nil 63 | } 64 | -------------------------------------------------------------------------------- /bind/doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package bind provides tools to generate bindings to use Go from Python. 6 | package bind 7 | -------------------------------------------------------------------------------- /bind/gen_func.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "fmt" 9 | "go/types" 10 | "strings" 11 | ) 12 | 13 | func (g *pyGen) recurse(gotype types.Type, prefix, name string) { 14 | switch t := gotype.(type) { 15 | case *types.Basic: 16 | // pass 17 | case *types.Map: 18 | g.recurse(t.Elem(), prefix, "Map_"+t.Key().String()+"_string_elem") 19 | case *types.Named: 20 | o := t.Obj() 21 | if o == nil || o.Pkg() == nil { 22 | return 23 | } 24 | g.recurse(t.Underlying(), prefix, o.Pkg().Name()+"_"+o.Name()) 25 | case *types.Struct: 26 | for i := 0; i < t.NumFields(); i++ { 27 | f := t.Field(i) 28 | g.recurse(f.Type(), prefix, name+"_"+f.Name()+"_Get") 29 | } 30 | case *types.Slice: 31 | g.recurse(t.Elem(), prefix, "Slice_string_elem") 32 | } 33 | } 34 | 35 | // genFuncSig generates just the signature for binding 36 | // returns false if function is not suitable for python 37 | // binding (e.g., multiple return values) 38 | func (g *pyGen) genFuncSig(sym *symbol, fsym *Func) bool { 39 | isMethod := (sym != nil) 40 | 41 | if fsym.sig == nil { 42 | return false 43 | } 44 | 45 | gname := fsym.GoName() 46 | if g.cfg.RenameCase { 47 | gname = toSnakeCase(gname) 48 | } 49 | 50 | gname, gdoc, err := extractPythonName(gname, fsym.Doc()) 51 | if err != nil { 52 | return false 53 | } 54 | ifchandle, gdoc := isIfaceHandle(gdoc) 55 | 56 | sig := fsym.sig 57 | args := sig.Params() 58 | res := sig.Results() 59 | nargs := 0 60 | nres := len(res) 61 | 62 | // note: this is enforced in creation of Func, in newFuncFrom 63 | if nres > 2 { 64 | return false 65 | } 66 | if nres == 2 && !fsym.err { 67 | return false 68 | } 69 | 70 | var ( 71 | goArgs []string 72 | pyArgs []string 73 | wpArgs []string 74 | ) 75 | 76 | if isMethod { 77 | goArgs = append(goArgs, "_handle CGoHandle") 78 | pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '_handle')", PyHandle)) 79 | wpArgs = append(wpArgs, "self") 80 | } 81 | 82 | nargs = len(args) 83 | for i := 0; i < nargs; i++ { 84 | arg := args[i] 85 | sarg := current.symtype(arg.GoType()) 86 | if sarg == nil { 87 | return false 88 | } 89 | anm := pySafeArg(arg.Name(), i) 90 | 91 | if ifchandle && arg.sym.goname == "interface{}" { 92 | goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, CGoHandle)) 93 | pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", PyHandle, anm)) 94 | } else { 95 | goArgs = append(goArgs, fmt.Sprintf("%s %s", anm, sarg.cgoname)) 96 | if sarg.cpyname == "PyObject*" { 97 | pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s', transfer_ownership=False)", sarg.cpyname, anm)) 98 | } else { 99 | pyArgs = append(pyArgs, fmt.Sprintf("param('%s', '%s')", sarg.cpyname, anm)) 100 | } 101 | } 102 | 103 | if i != nargs-1 || !fsym.isVariadic { 104 | wpArgs = append(wpArgs, anm) 105 | } 106 | } 107 | 108 | // support for optional arg to run in a separate go routine -- only if no return val 109 | if nres == 0 { 110 | goArgs = append(goArgs, "goRun C.char") 111 | pyArgs = append(pyArgs, "param('bool', 'goRun')") 112 | wpArgs = append(wpArgs, "goRun=False") 113 | } 114 | 115 | // To support variadic args, we add *args at the end. 116 | if fsym.isVariadic { 117 | wpArgs = append(wpArgs, "*args") 118 | } 119 | 120 | // When building the pybindgen builder code, we start with 121 | // a function that adds function calls with exception checking. 122 | // But given specific return types, we may want to add more 123 | // behavior to the wrapped function code gen. 124 | addFuncName := "add_checked_function" 125 | if len(res) > 0 { 126 | ret := res[0] 127 | switch t := ret.GoType().(type) { 128 | case *types.Basic: 129 | // string return types need special memory leak patches 130 | // to free the allocated char* 131 | if t.Kind() == types.String { 132 | addFuncName = "add_checked_string_function" 133 | } 134 | } 135 | } 136 | 137 | switch { 138 | case isMethod: 139 | mnm := sym.id + "_" + fsym.GoName() 140 | 141 | g.gofile.Printf("\n//export %s\n", mnm) 142 | g.gofile.Printf("func %s(", mnm) 143 | 144 | g.pybuild.Printf("%s(mod, '%s', ", addFuncName, mnm) 145 | 146 | g.pywrap.Printf("def %s(", gname) 147 | default: 148 | g.gofile.Printf("\n//export %s\n", fsym.ID()) 149 | g.gofile.Printf("func %s(", fsym.ID()) 150 | 151 | g.pybuild.Printf("%s(mod, '%s', ", addFuncName, fsym.ID()) 152 | 153 | g.pywrap.Printf("def %s(", gname) 154 | } 155 | 156 | goRet := "" 157 | nres = len(res) 158 | if nres > 0 { 159 | ret := res[0] 160 | sret := current.symtype(ret.GoType()) 161 | if sret == nil { 162 | panic(fmt.Errorf( 163 | "gopy: could not find symbol for %q", 164 | ret.Name(), 165 | )) 166 | } 167 | 168 | if sret.cpyname == "PyObject*" { 169 | g.pybuild.Printf("retval('%s', caller_owns_return=True)", sret.cpyname) 170 | } else { 171 | g.pybuild.Printf("retval('%s')", sret.cpyname) 172 | } 173 | goRet = fmt.Sprintf("%s", sret.cgoname) 174 | } else { 175 | g.pybuild.Printf("None") 176 | } 177 | 178 | if len(goArgs) > 0 { 179 | gstr := strings.Join(goArgs, ", ") 180 | g.gofile.Printf("%v) %v", gstr, goRet) 181 | 182 | pstr := strings.Join(pyArgs, ", ") 183 | g.pybuild.Printf(", [%v])\n", pstr) 184 | 185 | wstr := strings.Join(wpArgs, ", ") 186 | g.pywrap.Printf("%v)", wstr) 187 | 188 | } else { 189 | g.gofile.Printf(") %v", goRet) 190 | 191 | g.pybuild.Printf(", [])\n") 192 | 193 | g.pywrap.Printf(")") 194 | } 195 | return true 196 | } 197 | 198 | func (g *pyGen) genFunc(o *Func) { 199 | if g.genFuncSig(nil, o) { 200 | g.genFuncBody(nil, o) 201 | } 202 | } 203 | 204 | func (g *pyGen) genMethod(s *symbol, o *Func) { 205 | if g.genFuncSig(s, o) { 206 | g.genFuncBody(s, o) 207 | } 208 | } 209 | 210 | func isIfaceHandle(gdoc string) (bool, string) { 211 | const PythonIface = "gopy:interface=handle" 212 | if idx := strings.Index(gdoc, PythonIface); idx >= 0 { 213 | gdoc = gdoc[:idx] + gdoc[idx+len(PythonIface)+1:] 214 | return true, gdoc 215 | } 216 | return false, gdoc 217 | } 218 | 219 | func (g *pyGen) genFuncBody(sym *symbol, fsym *Func) { 220 | isMethod := (sym != nil) 221 | isIface := false 222 | symNm := "" 223 | if isMethod { 224 | symNm = sym.goname 225 | isIface = sym.isInterface() 226 | if !isIface { 227 | symNm = "*" + symNm 228 | } 229 | } 230 | 231 | pkgname := g.cfg.Name 232 | 233 | _, gdoc, _ := extractPythonName(fsym.GoName(), fsym.Doc()) 234 | ifchandle, gdoc := isIfaceHandle(gdoc) 235 | 236 | sig := fsym.Signature() 237 | res := sig.Results() 238 | args := sig.Params() 239 | nres := len(res) 240 | 241 | rvIsErr := false // set to true if the main return is an error 242 | if nres == 1 { 243 | ret := res[0] 244 | if isErrorType(ret.GoType()) { 245 | rvIsErr = true 246 | } 247 | } 248 | 249 | g.pywrap.Printf(":\n") 250 | g.pywrap.Indent() 251 | g.pywrap.Printf(`"""%s"""`, gdoc) 252 | g.pywrap.Printf("\n") 253 | 254 | g.gofile.Printf(" {\n") 255 | g.gofile.Indent() 256 | if fsym.hasfun { 257 | for i, arg := range args { 258 | if arg.sym.isSignature() { 259 | g.gofile.Printf("_fun_arg := %s\n", pySafeArg(arg.Name(), i)) 260 | } 261 | } 262 | } 263 | 264 | // release GIL 265 | g.gofile.Printf("_saved_thread := C.PyEval_SaveThread()\n") 266 | if !rvIsErr && nres != 2 { 267 | // reacquire GIL after return 268 | g.gofile.Printf("defer C.PyEval_RestoreThread(_saved_thread)\n") 269 | } 270 | 271 | if isMethod { 272 | g.gofile.Printf( 273 | `vifc, __err := gopyh.VarFromHandleTry((gopyh.CGoHandle)(_handle), "%s") 274 | if __err != nil { 275 | `, symNm) 276 | g.gofile.Indent() 277 | if nres > 0 { 278 | ret := res[0] 279 | if ret.sym.zval == "" { 280 | fmt.Printf("gopy: programmer error: empty zval zero value in symbol: %v\n", ret.sym) 281 | } 282 | if ret.sym.go2py != "" { 283 | g.gofile.Printf("return %s(%s)%s\n", ret.sym.go2py, ret.sym.zval, ret.sym.go2pyParenEx) 284 | } else { 285 | g.gofile.Printf("return %s\n", ret.sym.zval) 286 | } 287 | } else { 288 | g.gofile.Printf("return\n") 289 | } 290 | g.gofile.Outdent() 291 | g.gofile.Printf("}\n") 292 | } else if rvIsErr { 293 | g.gofile.Printf("var __err error\n") 294 | } 295 | 296 | callArgs := []string{} 297 | wrapArgs := []string{} 298 | if isMethod { 299 | wrapArgs = append(wrapArgs, "self.handle") 300 | } 301 | for i, arg := range args { 302 | na := "" 303 | anm := pySafeArg(arg.Name(), i) 304 | switch { 305 | case ifchandle && arg.sym.goname == "interface{}": 306 | na = fmt.Sprintf(`gopyh.VarFromHandle((gopyh.CGoHandle)(%s), "interface{}")`, anm) 307 | case arg.sym.isSignature(): 308 | na = fmt.Sprintf("%s", arg.sym.py2go) 309 | case arg.sym.py2go != "": 310 | na = fmt.Sprintf("%s(%s)%s", arg.sym.py2go, anm, arg.sym.py2goParenEx) 311 | default: 312 | na = anm 313 | } 314 | if i == len(args)-1 && fsym.isVariadic { 315 | na = na + "..." 316 | } 317 | callArgs = append(callArgs, na) 318 | switch { 319 | case arg.sym.goname == "interface{}": 320 | if ifchandle { 321 | wrapArgs = append(wrapArgs, fmt.Sprintf("%s.handle", anm)) 322 | } else { 323 | wrapArgs = append(wrapArgs, anm) 324 | } 325 | case arg.sym.hasHandle(): 326 | wrapArgs = append(wrapArgs, fmt.Sprintf("%s.handle", anm)) 327 | default: 328 | wrapArgs = append(wrapArgs, anm) 329 | } 330 | 331 | // To support variadic args, we add *args at the end. 332 | if fsym.isVariadic && i == len(args)-1 { 333 | packagePrefix := "" 334 | if arg.sym.gopkg.Name() != fsym.pkg.Name() { 335 | packagePrefix = arg.sym.gopkg.Name() + "." 336 | } 337 | g.pywrap.Printf("%s = %s%s(args)\n", anm, packagePrefix, arg.sym.id) 338 | } 339 | } 340 | 341 | // pywrap output 342 | mnm := fsym.ID() 343 | if isMethod { 344 | mnm = sym.id + "_" + fsym.GoName() 345 | } 346 | rvHasHandle := false 347 | if nres > 0 { 348 | ret := res[0] 349 | if !rvIsErr && ret.sym.hasHandle() { 350 | rvHasHandle = true 351 | cvnm := ret.sym.pyPkgId(g.pkg.pkg) 352 | g.pywrap.Printf("return %s(handle=_%s.%s(", cvnm, pkgname, mnm) 353 | } else { 354 | g.pywrap.Printf("return _%s.%s(", pkgname, mnm) 355 | } 356 | } else { 357 | g.pywrap.Printf("_%s.%s(", pkgname, mnm) 358 | } 359 | 360 | hasRetCvt := false 361 | hasAddrOfTmp := false 362 | if nres > 0 { 363 | ret := res[0] 364 | switch { 365 | case rvIsErr: 366 | g.gofile.Printf("__err = ") 367 | case nres == 2: 368 | g.gofile.Printf("cret, __err := ") 369 | case ret.sym.hasHandle() && !ret.sym.isPtrOrIface(): 370 | hasAddrOfTmp = true 371 | g.gofile.Printf("cret := ") 372 | case ret.sym.go2py != "": 373 | hasRetCvt = true 374 | g.gofile.Printf("return %s(", ret.sym.go2py) 375 | default: 376 | g.gofile.Printf("return ") 377 | } 378 | } 379 | if nres == 0 { 380 | wrapArgs = append(wrapArgs, "goRun") 381 | } 382 | g.pywrap.Printf("%s)", strings.Join(wrapArgs, ", ")) 383 | if rvHasHandle { 384 | g.pywrap.Printf(")") 385 | } 386 | 387 | funCall := "" 388 | if isMethod { 389 | if sym.isStruct() { 390 | funCall = fmt.Sprintf("gopyh.Embed(vifc, reflect.TypeOf(%s{})).(%s).%s(%s)", nonPtrName(symNm), symNm, fsym.GoName(), strings.Join(callArgs, ", ")) 391 | } else { 392 | funCall = fmt.Sprintf("vifc.(%s).%s(%s)", symNm, fsym.GoName(), strings.Join(callArgs, ", ")) 393 | } 394 | } else { 395 | funCall = fmt.Sprintf("%s(%s)", fsym.GoFmt(), strings.Join(callArgs, ", ")) 396 | } 397 | if hasRetCvt { 398 | ret := res[0] 399 | funCall += fmt.Sprintf(")%s", ret.sym.go2pyParenEx) 400 | } 401 | 402 | if nres == 0 { 403 | g.gofile.Printf("if boolPyToGo(goRun) {\n") 404 | g.gofile.Indent() 405 | g.gofile.Printf("go %s\n", funCall) 406 | g.gofile.Outdent() 407 | g.gofile.Printf("} else {\n") 408 | g.gofile.Indent() 409 | g.gofile.Printf("%s\n", funCall) 410 | g.gofile.Outdent() 411 | g.gofile.Printf("}") 412 | } else { 413 | g.gofile.Printf("%s\n", funCall) 414 | } 415 | 416 | if rvIsErr || nres == 2 { 417 | g.gofile.Printf("\n") 418 | // reacquire GIL 419 | g.gofile.Printf("C.PyEval_RestoreThread(_saved_thread)\n") 420 | 421 | g.gofile.Printf("if __err != nil {\n") 422 | g.gofile.Indent() 423 | g.gofile.Printf("estr := C.CString(__err.Error())\n") 424 | g.gofile.Printf("C.PyErr_SetString(C.PyExc_RuntimeError, estr)\n") 425 | if rvIsErr { 426 | g.gofile.Printf("return estr\n") // NOTE: leaked string 427 | } else { 428 | g.gofile.Printf("C.free(unsafe.Pointer(estr))\n") // python should have converted, safe 429 | ret := res[0] 430 | if ret.sym.zval == "" { 431 | fmt.Printf("gopy: programmer error: empty zval zero value in symbol: %v\n", ret.sym) 432 | } 433 | if ret.sym.go2py != "" { 434 | g.gofile.Printf("return %s(%s)%s\n", ret.sym.go2py, ret.sym.zval, ret.sym.go2pyParenEx) 435 | } else { 436 | g.gofile.Printf("return %s\n", ret.sym.zval) 437 | } 438 | } 439 | g.gofile.Outdent() 440 | g.gofile.Printf("}\n") 441 | if rvIsErr { 442 | g.gofile.Printf("return C.CString(\"\")") // NOTE: leaked string 443 | } else { 444 | ret := res[0] 445 | if ret.sym.go2py != "" { 446 | if ret.sym.hasHandle() && !ret.sym.isPtrOrIface() { 447 | g.gofile.Printf("return %s(&cret)%s", ret.sym.go2py, ret.sym.go2pyParenEx) 448 | } else { 449 | g.gofile.Printf("return %s(cret)%s", ret.sym.go2py, ret.sym.go2pyParenEx) 450 | } 451 | } else { 452 | g.gofile.Printf("return cret") 453 | } 454 | } 455 | } else if hasAddrOfTmp { 456 | ret := res[0] 457 | g.gofile.Printf("\nreturn %s(&cret)%s", ret.sym.go2py, ret.sym.go2pyParenEx) 458 | } 459 | g.gofile.Printf("\n") 460 | g.gofile.Outdent() 461 | g.gofile.Printf("}\n") 462 | 463 | g.pywrap.Printf("\n") 464 | g.pywrap.Outdent() 465 | } 466 | -------------------------------------------------------------------------------- /bind/gen_struct.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "fmt" 9 | "go/types" 10 | ) 11 | 12 | func (g *pyGen) genStruct(s *Struct) { 13 | strNm := s.obj.Name() 14 | 15 | base := "go.GoClass" 16 | emb := s.FirstEmbed() 17 | if emb != nil { 18 | base = emb.pyPkgId(s.sym.gopkg) 19 | } 20 | 21 | g.pywrap.Printf(` 22 | # Python type for struct %[3]s 23 | class %[1]s(%[4]s): 24 | ""%[2]q"" 25 | `, 26 | strNm, 27 | s.Doc(), 28 | s.GoName(), 29 | base, 30 | ) 31 | g.pywrap.Indent() 32 | g.genStructInit(s) 33 | g.genStructMembers(s) 34 | g.genStructMethods(s) 35 | g.pywrap.Outdent() 36 | } 37 | 38 | func (g *pyGen) genStructInit(s *Struct) { 39 | pkgname := g.cfg.Name 40 | qNm := s.GoName() 41 | // strNm := s.obj.Name() 42 | 43 | numFields := s.Struct().NumFields() 44 | 45 | g.pywrap.Printf("def __init__(self, *args, **kwargs):\n") 46 | g.pywrap.Indent() 47 | g.pywrap.Printf(`""" 48 | handle=A Go-side object is always initialized with an explicit handle=arg 49 | otherwise parameters can be unnamed in order of field names or named fields 50 | in which case a new Go object is constructed first 51 | """ 52 | `) 53 | g.pywrap.Printf("if len(kwargs) == 1 and 'handle' in kwargs:\n") 54 | g.pywrap.Indent() 55 | g.pywrap.Printf("self.handle = kwargs['handle']\n") 56 | g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.pypkgname) 57 | g.pywrap.Outdent() 58 | g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], go.GoClass):\n") 59 | g.pywrap.Indent() 60 | g.pywrap.Printf("self.handle = args[0].handle\n") 61 | g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.pypkgname) 62 | g.pywrap.Outdent() 63 | g.pywrap.Printf("else:\n") 64 | g.pywrap.Indent() 65 | g.pywrap.Printf("self.handle = _%s.%s_CTor()\n", pkgname, s.ID()) 66 | g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.pypkgname) 67 | 68 | for i := 0; i < numFields; i++ { 69 | f := s.Struct().Field(i) 70 | if _, err := isPyCompatField(f); err != nil { 71 | continue 72 | } 73 | // NOTE: this will accept int args for any handles / object fields so 74 | // some kind of additional type-checking logic to prevent that in a way 75 | // that also allows valid handles to be used as required. This is 76 | // achieved in the per-field setters (see below) with checks to ensure 77 | // that a struct field that is a gopy managed object is only 78 | // assigned gopy managed objects. Fields of basic types (e.g int, string) 79 | // etc can be assigned to directly. 80 | g.pywrap.Printf("if %[1]d < len(args):\n", i) 81 | g.pywrap.Indent() 82 | g.pywrap.Printf("self.%s = args[%d]\n", f.Name(), i) 83 | g.pywrap.Outdent() 84 | g.pywrap.Printf("if %[1]q in kwargs:\n", f.Name()) 85 | g.pywrap.Indent() 86 | g.pywrap.Printf("self.%[1]s = kwargs[%[1]q]\n", f.Name()) 87 | g.pywrap.Outdent() 88 | } 89 | g.pywrap.Outdent() 90 | g.pywrap.Outdent() 91 | 92 | g.pywrap.Printf("def __del__(self):\n") 93 | g.pywrap.Indent() 94 | g.pywrap.Printf("_%s.DecRef(self.handle)\n", g.pypkgname) 95 | g.pywrap.Outdent() 96 | 97 | if s.prots&ProtoStringer != 0 { 98 | for _, m := range s.meths { 99 | if !isStringer(m.obj) { 100 | continue 101 | } 102 | g.pywrap.Printf("def __str__(self):\n") 103 | g.pywrap.Indent() 104 | g.genStringerCall() 105 | g.pywrap.Outdent() 106 | g.pywrap.Printf("\n") 107 | } 108 | } else { 109 | g.pywrap.Printf("def __str__(self):\n") 110 | g.pywrap.Indent() 111 | g.pywrap.Printf("pr = [(p, getattr(self, p)) for p in dir(self) if not p.startswith('__')]\n") 112 | // g.pywrap.Printf("print(pr)\n") 113 | g.pywrap.Printf("sv = '%s{'\n", qNm) 114 | g.pywrap.Printf("first = True\n") 115 | g.pywrap.Printf("for v in pr:\n") 116 | g.pywrap.Indent() 117 | g.pywrap.Printf("if callable(v[1]):\n") 118 | g.pywrap.Indent() 119 | g.pywrap.Printf("continue\n") 120 | g.pywrap.Outdent() 121 | g.pywrap.Printf("if first:\n") 122 | g.pywrap.Indent() 123 | g.pywrap.Printf("first = False\n") 124 | g.pywrap.Outdent() 125 | g.pywrap.Printf("else:\n") 126 | g.pywrap.Indent() 127 | g.pywrap.Printf("sv += ', '\n") 128 | g.pywrap.Outdent() 129 | g.pywrap.Printf("sv += v[0] + '=' + str(v[1])\n") 130 | g.pywrap.Outdent() 131 | g.pywrap.Printf("return sv + '}'\n") 132 | g.pywrap.Outdent() 133 | } 134 | 135 | g.pywrap.Printf("def __repr__(self):\n") 136 | g.pywrap.Indent() 137 | g.pywrap.Printf("pr = [(p, getattr(self, p)) for p in dir(self) if not p.startswith('__')]\n") 138 | g.pywrap.Printf("sv = '%s ( '\n", qNm) 139 | g.pywrap.Printf("for v in pr:\n") 140 | g.pywrap.Indent() 141 | g.pywrap.Printf("if not callable(v[1]):\n") 142 | g.pywrap.Indent() 143 | g.pywrap.Printf("sv += v[0] + '=' + str(v[1]) + ', '\n") 144 | g.pywrap.Outdent() 145 | g.pywrap.Outdent() 146 | g.pywrap.Printf("return sv + ')'\n") 147 | g.pywrap.Outdent() 148 | 149 | // go ctor 150 | ctNm := s.ID() + "_CTor" 151 | g.gofile.Printf("\n// --- wrapping struct: %v ---\n", qNm) 152 | g.gofile.Printf("//export %s\n", ctNm) 153 | g.gofile.Printf("func %s() CGoHandle {\n", ctNm) 154 | g.gofile.Indent() 155 | g.gofile.Printf("return CGoHandle(handleFromPtr_%s(&%s{}))\n", s.ID(), qNm) 156 | g.gofile.Outdent() 157 | g.gofile.Printf("}\n") 158 | 159 | g.pybuild.Printf("mod.add_function('%s', retval('%s'), [])\n", ctNm, PyHandle) 160 | 161 | } 162 | 163 | func (g *pyGen) genStructMembers(s *Struct) { 164 | typ := s.Struct() 165 | for i := 0; i < typ.NumFields(); i++ { 166 | f := typ.Field(i) 167 | ftyp, err := isPyCompatField(f) 168 | if err != nil { 169 | continue 170 | } 171 | g.genStructMemberGetter(s, i, f) 172 | if !ftyp.isArray() { 173 | g.genStructMemberSetter(s, i, f) 174 | } 175 | } 176 | } 177 | 178 | func (g *pyGen) genStructMemberGetter(s *Struct, i int, f types.Object) { 179 | pkgname := g.cfg.Name 180 | ft := f.Type() 181 | ret := current.symtype(ft) 182 | if ret == nil { 183 | return 184 | } 185 | 186 | gname := f.Name() 187 | if g.cfg.RenameCase { 188 | gname = toSnakeCase(gname) 189 | } 190 | 191 | if newName, err := extractPythonNameFieldTag(gname, s.Struct().Tag(i)); err == nil { 192 | gname = newName 193 | } 194 | 195 | cgoFn := fmt.Sprintf("%s_%s_Get", s.ID(), f.Name()) 196 | 197 | g.pywrap.Printf("@property\n") 198 | g.pywrap.Printf("def %[1]s(self):\n", gname) 199 | g.pywrap.Indent() 200 | if gdoc := g.pkg.getDoc(s.Obj().Name(), f); gdoc != "" { 201 | g.pywrap.Printf(`"""`) 202 | g.pywrap.Printf(gdoc) 203 | g.pywrap.Println(`"""`) 204 | } 205 | if ret.hasHandle() { 206 | cvnm := ret.pyPkgId(g.pkg.pkg) 207 | g.pywrap.Printf("return %s(handle=_%s.%s(self.handle))\n", cvnm, pkgname, cgoFn) 208 | } else { 209 | g.pywrap.Printf("return _%s.%s(self.handle)\n", pkgname, cgoFn) 210 | } 211 | g.pywrap.Outdent() 212 | 213 | g.gofile.Printf("//export %s\n", cgoFn) 214 | g.gofile.Printf("func %s(handle CGoHandle) %s {\n", cgoFn, ret.cgoname) 215 | g.gofile.Indent() 216 | g.gofile.Printf("op := ptrFromHandle_%s(handle)\nreturn ", s.ID()) 217 | if ret.go2py != "" { 218 | if ret.hasHandle() && !ret.isPtrOrIface() { 219 | g.gofile.Printf("%s(&op.%s)%s", ret.go2py, f.Name(), ret.go2pyParenEx) 220 | } else { 221 | g.gofile.Printf("%s(op.%s)%s", ret.go2py, f.Name(), ret.go2pyParenEx) 222 | } 223 | } else { 224 | g.gofile.Printf("op.%s", f.Name()) 225 | } 226 | g.gofile.Printf("\n") 227 | g.gofile.Outdent() 228 | g.gofile.Printf("}\n\n") 229 | 230 | g.pybuild.Printf("mod.add_function('%s', retval('%s'), [param('%s', 'handle')])\n", cgoFn, ret.cpyname, PyHandle) 231 | } 232 | 233 | func (g *pyGen) genStructMemberSetter(s *Struct, i int, f types.Object) { 234 | pkgname := g.cfg.Name 235 | ft := f.Type() 236 | ret := current.symtype(ft) 237 | if ret == nil { 238 | return 239 | } 240 | 241 | gname := f.Name() 242 | if g.cfg.RenameCase { 243 | gname = toSnakeCase(gname) 244 | } 245 | 246 | if newName, err := extractPythonNameFieldTag(gname, s.Struct().Tag(i)); err == nil { 247 | gname = newName 248 | } 249 | 250 | cgoFn := fmt.Sprintf("%s_%s_Set", s.ID(), f.Name()) 251 | 252 | g.pywrap.Printf("@%s.setter\n", gname) 253 | g.pywrap.Printf("def %[1]s(self, value):\n", gname) 254 | g.pywrap.Indent() 255 | g.pywrap.Printf("if isinstance(value, go.GoClass):\n") 256 | g.pywrap.Indent() 257 | g.pywrap.Printf("_%s.%s(self.handle, value.handle)\n", pkgname, cgoFn) 258 | g.pywrap.Outdent() 259 | g.pywrap.Printf("else:\n") 260 | g.pywrap.Indent() 261 | // See comment in genStructInit about ensuring that gopy managed 262 | // objects are only assigned to from gopy managed objects. 263 | utyp := f.Type() 264 | if _, isNamed := utyp.(*types.Named); isNamed { 265 | utyp = utyp.Underlying() 266 | } 267 | switch utyp.(type) { 268 | case *types.Basic: 269 | g.pywrap.Printf("_%s.%s(self.handle, value)\n", pkgname, cgoFn) 270 | default: 271 | g.pywrap.Printf("raise TypeError(\"supplied argument type {t} is not a go.GoClass\".format(t=type(value)))\n") 272 | } 273 | g.pywrap.Outdent() 274 | g.pywrap.Outdent() 275 | 276 | g.gofile.Printf("//export %s\n", cgoFn) 277 | g.gofile.Printf("func %s(handle CGoHandle, val %s) {\n", cgoFn, ret.cgoname) 278 | g.gofile.Indent() 279 | g.gofile.Printf("op := ptrFromHandle_%s(handle)\n", s.ID()) 280 | if ret.py2go != "" { 281 | g.gofile.Printf("op.%s = %s(val)%s", f.Name(), ret.py2go, ret.py2goParenEx) 282 | } else { 283 | g.gofile.Printf("op.%s = val", f.Name()) 284 | } 285 | g.gofile.Printf("\n") 286 | g.gofile.Outdent() 287 | g.gofile.Printf("}\n\n") 288 | 289 | g.pybuild.Printf("mod.add_function('%s', None, [param('%s', 'handle'), param('%s', 'val')])\n", cgoFn, PyHandle, ret.cpyname) 290 | } 291 | 292 | func (g *pyGen) genStructMethods(s *Struct) { 293 | for _, m := range s.meths { 294 | g.genMethod(s.sym, m) 295 | } 296 | } 297 | 298 | ////////////////////////////////////////////////////////////////////////// 299 | // Interface 300 | 301 | func (g *pyGen) genInterface(ifc *Interface) { 302 | strNm := ifc.obj.Name() 303 | g.pywrap.Printf(` 304 | # Python type for interface %[3]s 305 | class %[1]s(go.GoClass): 306 | ""%[2]q"" 307 | `, 308 | strNm, 309 | ifc.Doc(), 310 | ifc.GoName(), 311 | ) 312 | g.pywrap.Indent() 313 | g.genIfaceInit(ifc) 314 | g.genIfaceMethods(ifc) 315 | g.pywrap.Outdent() 316 | } 317 | 318 | func (g *pyGen) genIfaceInit(ifc *Interface) { 319 | g.pywrap.Printf("def __init__(self, *args, **kwargs):\n") 320 | g.pywrap.Indent() 321 | g.pywrap.Printf(`""" 322 | handle=A Go-side object is always initialized with an explicit handle=arg 323 | """ 324 | `) 325 | 326 | g.pywrap.Printf("if len(kwargs) == 1 and 'handle' in kwargs:\n") 327 | g.pywrap.Indent() 328 | g.pywrap.Printf("self.handle = kwargs['handle']\n") 329 | g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.pypkgname) 330 | g.pywrap.Outdent() 331 | g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], go.GoClass):\n") 332 | g.pywrap.Indent() 333 | g.pywrap.Printf("self.handle = args[0].handle\n") 334 | g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.pypkgname) 335 | g.pywrap.Outdent() 336 | g.pywrap.Printf("else:\n") 337 | g.pywrap.Indent() 338 | g.pywrap.Printf("self.handle = 0\n") 339 | g.pywrap.Outdent() 340 | g.pywrap.Outdent() 341 | 342 | for _, m := range ifc.meths { 343 | if !isStringer(m.obj) { 344 | continue 345 | } 346 | g.pywrap.Printf("def __str__(self):\n") 347 | g.pywrap.Indent() 348 | g.genStringerCall() 349 | g.pywrap.Outdent() 350 | g.pywrap.Printf("\n") 351 | } 352 | } 353 | 354 | func (g *pyGen) genIfaceMethods(ifc *Interface) { 355 | for _, m := range ifc.meths { 356 | g.genMethod(ifc.sym, m) 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /bind/gen_type.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | // extTypes = these are types external to any targeted packages 8 | // pyWrapOnly = only generate python wrapper code, not go code 9 | func (g *pyGen) genType(sym *symbol, extTypes, pyWrapOnly bool) { 10 | if !sym.isType() { 11 | return 12 | } 13 | if sym.isBasic() && !sym.isNamed() { 14 | return 15 | } 16 | 17 | if sym.isNamedBasic() { 18 | // TODO: could have methods! 19 | return 20 | } 21 | 22 | // TODO: not handling yet: 23 | if sym.isSignature() { 24 | return 25 | } 26 | 27 | if !pyWrapOnly { 28 | switch { 29 | case sym.isPointer() || sym.isInterface(): 30 | g.genTypeHandlePtr(sym) 31 | case sym.isSlice() || sym.isMap() || sym.isArray(): 32 | g.genTypeHandleImplPtr(sym) 33 | default: 34 | g.genTypeHandle(sym) 35 | } 36 | } 37 | 38 | if extTypes { 39 | if sym.isSlice() || sym.isArray() { 40 | g.genSlice(sym, extTypes, pyWrapOnly, nil) 41 | } else if sym.isMap() { 42 | g.genMap(sym, extTypes, pyWrapOnly, nil) 43 | } else if sym.isInterface() || sym.isStruct() { 44 | if pyWrapOnly { 45 | g.genExtClass(sym) 46 | } 47 | } 48 | } else { 49 | if g.pkg == goPackage || !sym.isNamed() { // only named types are generated separately 50 | if sym.isSlice() { 51 | g.genSlice(sym, extTypes, pyWrapOnly, nil) 52 | } else if sym.isMap() { 53 | g.genMap(sym, extTypes, pyWrapOnly, nil) 54 | } 55 | } 56 | if sym.isArray() { 57 | g.genSlice(sym, extTypes, pyWrapOnly, nil) 58 | } 59 | } 60 | } 61 | 62 | func (g *pyGen) genTypeHandlePtr(sym *symbol) { 63 | if sym.goname == "interface{}" { 64 | return 65 | } 66 | gonm := sym.goname 67 | g.gofile.Printf("\n// Converters for pointer handles for type: %s\n", gonm) 68 | g.gofile.Printf("func %s(h CGoHandle) %s {\n", sym.py2go, gonm) 69 | g.gofile.Indent() 70 | g.gofile.Printf("p := gopyh.VarFromHandle((gopyh.CGoHandle)(h), %[1]q)\n", gonm) 71 | g.gofile.Printf("if p == nil {\n") 72 | g.gofile.Indent() 73 | g.gofile.Printf("return nil\n") 74 | g.gofile.Outdent() 75 | g.gofile.Printf("}\n") 76 | if sym.isStruct() { 77 | g.gofile.Printf("return gopyh.Embed(p, reflect.TypeOf(%s{})).(%s)\n", nonPtrName(gonm), gonm) 78 | } else { 79 | g.gofile.Printf("return p.(%s)\n", gonm) 80 | } 81 | g.gofile.Outdent() 82 | g.gofile.Printf("}\n") 83 | g.gofile.Printf("func %s(p interface{})%s CGoHandle {\n", sym.go2py, sym.go2pyParenEx) 84 | g.gofile.Indent() 85 | g.gofile.Printf("return CGoHandle(gopyh.Register(\"%s\", p))\n", gonm) 86 | g.gofile.Outdent() 87 | g.gofile.Printf("}\n") 88 | } 89 | 90 | // implicit pointer types: slice, map, array 91 | func (g *pyGen) genTypeHandleImplPtr(sym *symbol) { 92 | gonm := sym.goname 93 | ptrnm := gonm 94 | nptrnm := gonm 95 | if ptrnm[0] != '*' { 96 | ptrnm = "*" + ptrnm 97 | } else { 98 | nptrnm = gonm[1:] 99 | } 100 | g.gofile.Printf("\n// Converters for implicit pointer handles for type: %s\n", gonm) 101 | g.gofile.Printf("func ptrFromHandle_%s(h CGoHandle) %s {\n", sym.id, ptrnm) 102 | g.gofile.Indent() 103 | g.gofile.Printf("p := gopyh.VarFromHandle((gopyh.CGoHandle)(h), %[1]q)\n", gonm) 104 | g.gofile.Printf("if p == nil {\n") 105 | g.gofile.Indent() 106 | g.gofile.Printf("return nil\n") 107 | g.gofile.Outdent() 108 | g.gofile.Printf("}\n") 109 | g.gofile.Printf("return p.(%s)\n", ptrnm) 110 | g.gofile.Outdent() 111 | g.gofile.Printf("}\n") 112 | g.gofile.Printf("func deptrFromHandle_%s(h CGoHandle) %s {\n", sym.id, nptrnm) 113 | g.gofile.Indent() 114 | g.gofile.Printf("p := ptrFromHandle_%s(h)\n", sym.id) 115 | if !sym.isArray() { 116 | g.gofile.Printf("if p == nil {\n") 117 | g.gofile.Indent() 118 | g.gofile.Printf("return nil\n") 119 | g.gofile.Outdent() 120 | g.gofile.Printf("}\n") 121 | } 122 | g.gofile.Printf("return *p\n") 123 | g.gofile.Outdent() 124 | g.gofile.Printf("}\n") 125 | g.gofile.Printf("func %s(p interface{})%s CGoHandle {\n", sym.go2py, sym.go2pyParenEx) 126 | g.gofile.Indent() 127 | g.gofile.Printf("return CGoHandle(gopyh.Register(\"%s\", p))\n", gonm) 128 | g.gofile.Outdent() 129 | g.gofile.Printf("}\n") 130 | } 131 | 132 | func nonPtrName(nm string) string { 133 | if nm[0] == '*' { 134 | return nm[1:] 135 | } 136 | return nm 137 | } 138 | 139 | func (g *pyGen) genTypeHandle(sym *symbol) { 140 | gonm := sym.goname 141 | ptrnm := gonm 142 | if ptrnm[0] != '*' { 143 | ptrnm = "*" + ptrnm 144 | } 145 | py2go := nonPtrName(sym.py2go) 146 | g.gofile.Printf("\n// Converters for non-pointer handles for type: %s\n", gonm) 147 | g.gofile.Printf("func %s(h CGoHandle) %s {\n", py2go, ptrnm) 148 | g.gofile.Indent() 149 | g.gofile.Printf("p := gopyh.VarFromHandle((gopyh.CGoHandle)(h), %[1]q)\n", gonm) 150 | g.gofile.Printf("if p == nil {\n") 151 | g.gofile.Indent() 152 | g.gofile.Printf("return nil\n") 153 | g.gofile.Outdent() 154 | g.gofile.Printf("}\n") 155 | if sym.isStruct() { 156 | g.gofile.Printf("return gopyh.Embed(p, reflect.TypeOf(%s{})).(%s)\n", nonPtrName(gonm), ptrnm) 157 | } else { 158 | g.gofile.Printf("return p.(%s)\n", ptrnm) 159 | } 160 | g.gofile.Outdent() 161 | g.gofile.Printf("}\n") 162 | g.gofile.Printf("func %s(p interface{})%s CGoHandle {\n", sym.go2py, sym.go2pyParenEx) 163 | g.gofile.Indent() 164 | g.gofile.Printf("return CGoHandle(gopyh.Register(\"%s\", p))\n", gonm) 165 | g.gofile.Outdent() 166 | g.gofile.Printf("}\n") 167 | } 168 | 169 | // genExtClass generates minimal python wrappers for external classes (struct, interface, etc) 170 | func (g *pyGen) genExtClass(sym *symbol) { 171 | pkgname := sym.gopkg.Name() 172 | // note: all external wrapper classes are defined in base go.py module, so we exclude go. 173 | g.pywrap.Printf(` 174 | # Python type for %[4]s 175 | class %[2]s(GoClass): 176 | ""%[3]q"" 177 | `, 178 | pkgname, 179 | sym.id, 180 | sym.doc, 181 | sym.goname, 182 | ) 183 | g.pywrap.Indent() 184 | g.pywrap.Printf("def __init__(self, *args, **kwargs):\n") 185 | g.pywrap.Indent() 186 | g.pywrap.Printf(`""" 187 | handle=A Go-side object is always initialized with an explicit handle=arg 188 | """ 189 | `) 190 | g.pywrap.Printf("if len(kwargs) == 1 and 'handle' in kwargs:\n") 191 | g.pywrap.Indent() 192 | g.pywrap.Printf("self.handle = kwargs['handle']\n") 193 | g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.pypkgname) 194 | g.pywrap.Outdent() 195 | g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], GoClass):\n") 196 | g.pywrap.Indent() 197 | g.pywrap.Printf("self.handle = args[0].handle\n") 198 | g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.pypkgname) 199 | g.pywrap.Outdent() 200 | g.pywrap.Printf("elif len(args) == 1 and isinstance(args[0], int):\n") 201 | g.pywrap.Indent() 202 | g.pywrap.Printf("self.handle = args[0]\n") 203 | g.pywrap.Printf("_%s.IncRef(self.handle)\n", g.pypkgname) 204 | g.pywrap.Outdent() 205 | g.pywrap.Printf("else:\n") 206 | g.pywrap.Indent() 207 | g.pywrap.Printf("self.handle = 0\n") 208 | g.pywrap.Outdent() 209 | g.pywrap.Outdent() 210 | 211 | g.pywrap.Printf("def __del__(self):\n") 212 | g.pywrap.Indent() 213 | g.pywrap.Printf("_%s.DecRef(self.handle)\n", g.pypkgname) 214 | g.pywrap.Outdent() 215 | 216 | g.pywrap.Printf("\n") 217 | g.pywrap.Outdent() 218 | 219 | } 220 | -------------------------------------------------------------------------------- /bind/gen_varconst.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "fmt" 9 | "strings" 10 | ) 11 | 12 | func (g *pyGen) genConst(c *Const) { 13 | if isPyCompatVar(c.sym) != nil { 14 | return 15 | } 16 | if c.sym.isSignature() { 17 | return 18 | } 19 | g.genConstValue(c) 20 | } 21 | 22 | func (g *pyGen) genVar(v *Var) { 23 | if isPyCompatVar(v.sym) != nil { 24 | return 25 | } 26 | if v.sym.isSignature() { 27 | return 28 | } 29 | g.genVarGetter(v) 30 | if !v.sym.isArray() { 31 | g.genVarSetter(v) 32 | } 33 | } 34 | 35 | func (g *pyGen) genVarGetter(v *Var) { 36 | gopkg := g.pkg.Name() 37 | pkgname := g.cfg.Name 38 | cgoFn := v.Name() // plain name is the getter 39 | if g.cfg.RenameCase { 40 | cgoFn = toSnakeCase(cgoFn) 41 | } 42 | qCgoFn := gopkg + "_" + cgoFn 43 | qFn := "_" + pkgname + "." + qCgoFn 44 | qVn := gopkg + "." + v.Name() 45 | 46 | g.pywrap.Printf("def %s():\n", cgoFn) 47 | g.pywrap.Indent() 48 | g.pywrap.Printf("%s\n%s Gets Go Variable: %s\n%s\n%s\n", `"""`, cgoFn, qVn, v.doc, `"""`) 49 | if v.sym.hasHandle() { 50 | cvnm := v.sym.pyPkgId(g.pkg.pkg) 51 | g.pywrap.Printf("return %s(handle=%s())\n", cvnm, qFn) 52 | } else { 53 | g.pywrap.Printf("return %s()\n", qFn) 54 | } 55 | g.pywrap.Outdent() 56 | g.pywrap.Printf("\n") 57 | 58 | g.gofile.Printf("//export %s\n", qCgoFn) 59 | g.gofile.Printf("func %s() %s {\n", qCgoFn, v.sym.cgoname) 60 | g.gofile.Indent() 61 | g.gofile.Printf("return ") 62 | if v.sym.go2py != "" { 63 | if v.sym.hasHandle() && !v.sym.isPtrOrIface() { 64 | g.gofile.Printf("%s(&%s)%s", v.sym.go2py, qVn, v.sym.go2pyParenEx) 65 | } else { 66 | g.gofile.Printf("%s(%s)%s", v.sym.go2py, qVn, v.sym.go2pyParenEx) 67 | } 68 | } else { 69 | g.gofile.Printf("%s", qVn) 70 | } 71 | g.gofile.Printf("\n") 72 | g.gofile.Outdent() 73 | g.gofile.Printf("}\n\n") 74 | 75 | g.pybuild.Printf("mod.add_function('%s', retval('%s'), [])\n", qCgoFn, v.sym.cpyname) 76 | } 77 | 78 | func (g *pyGen) genVarSetter(v *Var) { 79 | gopkg := g.pkg.Name() 80 | pkgname := g.cfg.Name 81 | cgoFn := fmt.Sprintf("Set_%s", v.Name()) 82 | if g.cfg.RenameCase { 83 | cgoFn = toSnakeCase(cgoFn) 84 | } 85 | qCgoFn := gopkg + "_" + cgoFn 86 | qFn := "_" + pkgname + "." + qCgoFn 87 | qVn := gopkg + "." + v.Name() 88 | 89 | g.pywrap.Printf("def %s(value):\n", cgoFn) 90 | g.pywrap.Indent() 91 | g.pywrap.Printf("%s\n%s Sets Go Variable: %s\n%s\n%s\n", `"""`, cgoFn, qVn, v.doc, `"""`) 92 | g.pywrap.Printf("if isinstance(value, go.GoClass):\n") 93 | g.pywrap.Indent() 94 | g.pywrap.Printf("%s(value.handle)\n", qFn) 95 | g.pywrap.Outdent() 96 | g.pywrap.Printf("else:\n") 97 | g.pywrap.Indent() 98 | g.pywrap.Printf("%s(value)\n", qFn) 99 | g.pywrap.Outdent() 100 | g.pywrap.Outdent() 101 | g.pywrap.Printf("\n") 102 | 103 | g.gofile.Printf("//export %s\n", qCgoFn) 104 | g.gofile.Printf("func %s(val %s) {\n", qCgoFn, v.sym.cgoname) 105 | g.gofile.Indent() 106 | if v.sym.py2go != "" { 107 | g.gofile.Printf("%s = %s(val)%s", qVn, v.sym.py2go, v.sym.py2goParenEx) 108 | } else { 109 | g.gofile.Printf("%s = val", qVn) 110 | } 111 | g.gofile.Printf("\n") 112 | g.gofile.Outdent() 113 | g.gofile.Printf("}\n\n") 114 | 115 | g.pybuild.Printf("mod.add_function('%s', None, [param('%s', 'val')])\n", qCgoFn, v.sym.cpyname) 116 | } 117 | 118 | func (g *pyGen) genConstValue(c *Const) { 119 | // constants go directly into wrapper as-is 120 | val := c.val 121 | switch val { 122 | case "true": 123 | val = "True" 124 | case "false": 125 | val = "False" 126 | } 127 | g.pywrap.Printf("%s = %s\n", c.GoName(), val) 128 | if c.doc != "" { 129 | lns := strings.Split(c.doc, "\n") 130 | g.pywrap.Printf(`"""`) 131 | g.pywrap.Printf("\n") 132 | for _, l := range lns { 133 | g.pywrap.Printf("%s\n", l) 134 | } 135 | g.pywrap.Printf(`"""`) 136 | g.pywrap.Printf("\n") 137 | } 138 | } 139 | 140 | func (g *pyGen) genEnum(e *Enum) { 141 | g.pywrap.Printf("class %s(Enum):\n", e.typ.Obj().Name()) 142 | g.pywrap.Indent() 143 | doc := e.Doc() 144 | if doc != "" { 145 | lns := strings.Split(doc, "\n") 146 | g.pywrap.Printf(`"""`) 147 | g.pywrap.Printf("\n") 148 | for _, l := range lns { 149 | g.pywrap.Printf("%s\n", l) 150 | } 151 | g.pywrap.Printf(`"""`) 152 | g.pywrap.Printf("\n") 153 | } 154 | e.SortConsts() 155 | for _, c := range e.items { 156 | g.genConstValue(c) 157 | } 158 | g.pywrap.Outdent() 159 | 160 | // Go has each const value globally available within a given package 161 | // so to keep the code consistent, we redundantly generate the consts 162 | // again here. The Enum organization however is critical for organizing 163 | // the values under the type (making them accessible programmatically) 164 | g.pywrap.Printf("\n") 165 | for _, c := range e.items { 166 | g.genConstValue(c) 167 | } 168 | g.pywrap.Printf("\n") 169 | } 170 | -------------------------------------------------------------------------------- /bind/printer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | ) 11 | 12 | type printer struct { 13 | buf *bytes.Buffer 14 | indentEach []byte 15 | indentText []byte 16 | needIndent bool 17 | } 18 | 19 | func (p *printer) writeIndent() error { 20 | if !p.needIndent { 21 | return nil 22 | } 23 | p.needIndent = false 24 | _, err := p.buf.Write(p.indentText) 25 | return err 26 | } 27 | 28 | func (p *printer) Read(b []byte) (n int, err error) { 29 | return p.buf.Read(b) 30 | } 31 | 32 | func (p *printer) Write(b []byte) (n int, err error) { 33 | wrote := 0 34 | for len(b) > 0 { 35 | if err := p.writeIndent(); err != nil { 36 | return wrote, err 37 | } 38 | i := bytes.IndexByte(b, '\n') 39 | if i < 0 { 40 | break 41 | } 42 | n, err = p.buf.Write(b[0 : i+1]) 43 | wrote += n 44 | if err != nil { 45 | return wrote, err 46 | } 47 | b = b[i+1:] 48 | p.needIndent = true 49 | } 50 | if len(b) > 0 { 51 | n, err = p.buf.Write(b) 52 | wrote += n 53 | } 54 | return wrote, err 55 | } 56 | 57 | func (p *printer) Printf(format string, args ...interface{}) { 58 | if _, err := fmt.Fprintf(p, format, args...); err != nil { 59 | panic(fmt.Sprintf("printer: %v", err)) 60 | } 61 | } 62 | 63 | func (p *printer) Println(out string) { 64 | if _, err := fmt.Fprintln(p, out); err != nil { 65 | panic(fmt.Sprintf("printer: %v", err)) 66 | } 67 | } 68 | 69 | func (p *printer) Indent() { 70 | p.indentText = append(p.indentText, p.indentEach...) 71 | } 72 | 73 | func (p *printer) Outdent() { 74 | if len(p.indentText) > len(p.indentEach)-1 { 75 | p.indentText = p.indentText[len(p.indentEach):] 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /bind/printer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "bytes" 9 | "io" 10 | "reflect" 11 | "testing" 12 | ) 13 | 14 | func TestPrinter(t *testing.T) { 15 | decl := &printer{ 16 | buf: new(bytes.Buffer), 17 | indentEach: []byte("\t"), 18 | } 19 | 20 | impl := &printer{ 21 | buf: new(bytes.Buffer), 22 | indentEach: []byte("\t"), 23 | } 24 | 25 | decl.Printf("\ndecl 1\n") 26 | decl.Indent() 27 | decl.Printf(">>> decl-1\n") 28 | decl.Outdent() 29 | impl.Printf("impl 1\n") 30 | decl.Printf("decl 2\n") 31 | impl.Printf("impl 2\n") 32 | 33 | out := new(bytes.Buffer) 34 | _, err := io.Copy(out, decl) 35 | if err != nil { 36 | t.Fatalf("error: %v\n", err) 37 | } 38 | 39 | _, err = io.Copy(out, impl) 40 | if err != nil { 41 | t.Fatalf("error: %v\n", err) 42 | } 43 | 44 | want := ` 45 | decl 1 46 | >>> decl-1 47 | decl 2 48 | impl 1 49 | impl 2 50 | ` 51 | 52 | str := out.String() 53 | if !reflect.DeepEqual(str, want) { 54 | t.Fatalf("error:\nwant=%q\ngot =%q\n", want, str) 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /bind/stdtypes.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "go/types" 9 | "reflect" 10 | ) 11 | 12 | // goPackage is the fake package that contains all our standard slice / map 13 | // types that we export 14 | var goPackage *Package 15 | 16 | // makeGoPackage 17 | func makeGoPackage() { 18 | gopk := types.NewPackage("go", "go") 19 | goPackage = &Package{pkg: gopk, syms: universe, objs: map[string]Object{}} 20 | Packages = append(Packages, goPackage) 21 | } 22 | 23 | // addStdSliceMaps adds std Slice and Map types to universe 24 | func addStdSliceMaps() { 25 | makeGoPackage() 26 | gopk := goPackage.pkg 27 | sltyps := []string{"int", "int64", "int32", "int16", "int8", "uint", "uint64", "uint32", "uint16", "uint8", "bool", "byte", "rune", "float64", "float32", "string", "error"} 28 | for _, tn := range sltyps { 29 | universe.addSliceType(gopk, nil, types.NewSlice(universe.sym(tn).gotyp), skType, "Slice_"+tn, "[]"+tn) 30 | } 31 | } 32 | 33 | // stdBasicTypes returns the basic int, float etc types as symbols 34 | func stdBasicTypes() map[string]*symbol { 35 | look := types.Universe.Lookup 36 | syms := map[string]*symbol{ 37 | "bool": { 38 | gopkg: look("bool").Pkg(), 39 | goobj: look("bool"), 40 | gotyp: look("bool").Type(), 41 | kind: skType | skBasic, 42 | goname: "bool", 43 | id: "bool", 44 | cgoname: "C.char", 45 | cpyname: "bool", 46 | pysig: "bool", 47 | go2py: "boolGoToPy", 48 | py2go: "boolPyToGo", 49 | zval: "false", 50 | pyfmt: "", // TODO: 51 | }, 52 | 53 | "byte": { 54 | gopkg: look("byte").Pkg(), 55 | goobj: look("byte"), 56 | gotyp: look("byte").Type(), 57 | kind: skType | skBasic, 58 | goname: "byte", 59 | id: "int", 60 | cpyname: "uint8_t", 61 | cgoname: "C.char", 62 | pysig: "int", // FIXME(sbinet) py2/py3 63 | go2py: "C.char", 64 | py2go: "byte", 65 | zval: "0", 66 | pyfmt: "b", 67 | }, 68 | 69 | "int": { 70 | gopkg: look("int").Pkg(), 71 | goobj: look("int"), 72 | gotyp: look("int").Type(), 73 | kind: skType | skBasic, 74 | goname: "int", 75 | id: "int", 76 | cpyname: "int", 77 | cgoname: "C.long", // see below for 64 bit version 78 | pysig: "int", 79 | go2py: "C.long", 80 | py2go: "int", 81 | zval: "0", 82 | pyfmt: "i", 83 | }, 84 | 85 | "int8": { 86 | gopkg: look("int8").Pkg(), 87 | goobj: look("int8"), 88 | gotyp: look("int8").Type(), 89 | kind: skType | skBasic, 90 | goname: "int8", 91 | id: "int", 92 | cpyname: "int8_t", 93 | cgoname: "C.char", 94 | pysig: "int", 95 | go2py: "C.char", 96 | py2go: "int8", 97 | zval: "0", 98 | pyfmt: "b", 99 | }, 100 | 101 | "int16": { 102 | gopkg: look("int16").Pkg(), 103 | goobj: look("int16"), 104 | gotyp: look("int16").Type(), 105 | kind: skType | skBasic, 106 | goname: "int16", 107 | id: "int16", 108 | cpyname: "int16_t", 109 | cgoname: "C.short", 110 | pysig: "int", 111 | go2py: "C.short", 112 | py2go: "int16", 113 | zval: "0", 114 | pyfmt: "h", 115 | }, 116 | 117 | "int32": { 118 | gopkg: look("int32").Pkg(), 119 | goobj: look("int32"), 120 | gotyp: look("int32").Type(), 121 | kind: skType | skBasic, 122 | goname: "int32", 123 | id: "int", 124 | cpyname: "int32_t", 125 | cgoname: "C.long", 126 | pysig: "int", 127 | go2py: "C.long", 128 | py2go: "int32", 129 | zval: "0", 130 | pyfmt: "i", 131 | }, 132 | 133 | "int64": { 134 | gopkg: look("int64").Pkg(), 135 | goobj: look("int64"), 136 | gotyp: look("int64").Type(), 137 | kind: skType | skBasic, 138 | goname: "int64", 139 | id: "int64", 140 | cpyname: "int64_t", 141 | cgoname: "C.longlong", 142 | pysig: "long", 143 | go2py: "C.longlong", 144 | py2go: "int64", 145 | zval: "0", 146 | pyfmt: "k", 147 | }, 148 | 149 | "uint": { 150 | gopkg: look("uint").Pkg(), 151 | goobj: look("uint"), 152 | gotyp: look("uint").Type(), 153 | kind: skType | skBasic, 154 | goname: "uint", 155 | id: "uint", 156 | cpyname: "unsigned int", 157 | cgoname: "C.uint", 158 | pysig: "int", 159 | go2py: "C.uint", 160 | py2go: "uint", 161 | zval: "0", 162 | pyfmt: "I", 163 | }, 164 | 165 | "uint8": { 166 | gopkg: look("uint8").Pkg(), 167 | goobj: look("uint8"), 168 | gotyp: look("uint8").Type(), 169 | kind: skType | skBasic, 170 | goname: "uint8", 171 | id: "uint8", 172 | cpyname: "uint8_t", 173 | cgoname: "C.uchar", 174 | pysig: "int", 175 | go2py: "C.uchar", 176 | py2go: "uint8", 177 | zval: "0", 178 | pyfmt: "B", 179 | }, 180 | 181 | "uint16": { 182 | gopkg: look("uint16").Pkg(), 183 | goobj: look("uint16"), 184 | gotyp: look("uint16").Type(), 185 | kind: skType | skBasic, 186 | goname: "uint16", 187 | id: "uint16", 188 | cpyname: "uint16_t", 189 | cgoname: "C.ushort", 190 | pysig: "int", 191 | go2py: "C.ushort", 192 | py2go: "uint16", 193 | zval: "0", 194 | pyfmt: "H", 195 | }, 196 | 197 | "uint32": { 198 | gopkg: look("uint32").Pkg(), 199 | goobj: look("uint32"), 200 | gotyp: look("uint32").Type(), 201 | kind: skType | skBasic, 202 | goname: "uint32", 203 | id: "uint32", 204 | cpyname: "uint32_t", 205 | cgoname: "C.ulong", 206 | pysig: "long", 207 | go2py: "C.ulong", 208 | py2go: "uint32", 209 | zval: "0", 210 | pyfmt: "I", 211 | }, 212 | 213 | "uint64": { 214 | gopkg: look("uint64").Pkg(), 215 | goobj: look("uint64"), 216 | gotyp: look("uint64").Type(), 217 | kind: skType | skBasic, 218 | goname: "uint64", 219 | id: "uint64", 220 | cpyname: "uint64_t", 221 | cgoname: "C.ulonglong", 222 | pysig: "long", 223 | go2py: "C.ulonglong", 224 | py2go: "uint64", 225 | zval: "0", 226 | pyfmt: "K", 227 | }, 228 | 229 | "uintptr": { 230 | gopkg: look("uintptr").Pkg(), 231 | goobj: look("uintptr"), 232 | gotyp: look("uintptr").Type(), 233 | kind: skType | skBasic, 234 | goname: "uintptr", 235 | id: "uintptr", 236 | cpyname: "uint64_t", 237 | cgoname: "C.ulonglong", 238 | pysig: "long", 239 | go2py: "C.ulonglong", 240 | py2go: "uintptr", 241 | zval: "0", 242 | pyfmt: "K", 243 | }, 244 | 245 | "float32": { 246 | gopkg: look("float32").Pkg(), 247 | goobj: look("float32"), 248 | gotyp: look("float32").Type(), 249 | kind: skType | skBasic, 250 | goname: "float32", 251 | id: "float32", 252 | cpyname: "float", 253 | cgoname: "C.float", 254 | pysig: "float", 255 | go2py: "C.float", 256 | py2go: "float32", 257 | zval: "0", 258 | pyfmt: "f", 259 | }, 260 | 261 | "float64": { 262 | gopkg: look("float64").Pkg(), 263 | goobj: look("float64"), 264 | gotyp: look("float64").Type(), 265 | kind: skType | skBasic, 266 | goname: "float64", 267 | id: "float64", 268 | cpyname: "double", 269 | cgoname: "C.double", 270 | pysig: "float", 271 | go2py: "C.double", 272 | py2go: "float64", 273 | zval: "0", 274 | pyfmt: "d", 275 | }, 276 | 277 | "complex64": { 278 | gopkg: look("complex64").Pkg(), 279 | goobj: look("complex64"), 280 | gotyp: look("complex64").Type(), 281 | kind: skType | skBasic, 282 | goname: "complex64", 283 | id: "complex64", 284 | cpyname: "PyObject*", 285 | cgoname: "*C.PyObject", 286 | pysig: "complex", 287 | go2py: "complex64GoToPy", 288 | py2go: "complex64PyToGo", 289 | zval: "0", 290 | pyfmt: "O&", 291 | }, 292 | 293 | "complex128": { 294 | gopkg: look("complex128").Pkg(), 295 | goobj: look("complex128"), 296 | gotyp: look("complex128").Type(), 297 | kind: skType | skBasic, 298 | goname: "complex128", 299 | id: "complex128", 300 | cpyname: "PyObject*", 301 | cgoname: "*C.PyObject", 302 | pysig: "complex", 303 | go2py: "complex128GoToPy", 304 | py2go: "complex128PyToGo", 305 | zval: "0", 306 | pyfmt: "O&", 307 | }, 308 | 309 | "string": { 310 | gopkg: look("string").Pkg(), 311 | goobj: look("string"), 312 | gotyp: look("string").Type(), 313 | kind: skType | skBasic, 314 | goname: "string", 315 | id: "string", 316 | cpyname: "char*", 317 | cgoname: "*C.char", 318 | pysig: "str", 319 | go2py: "C.CString", 320 | py2go: "C.GoString", 321 | zval: `""`, 322 | pyfmt: "s", 323 | }, 324 | 325 | "rune": { // FIXME(sbinet) py2/py3 326 | gopkg: look("rune").Pkg(), 327 | goobj: look("rune"), 328 | gotyp: look("rune").Type(), 329 | kind: skType | skBasic, 330 | goname: "rune", 331 | id: "rune", 332 | cpyname: "int32_t", 333 | cgoname: "C.long", 334 | pysig: "str", 335 | go2py: "C.long", 336 | py2go: "rune", 337 | zval: "0", 338 | pyfmt: "i", 339 | }, 340 | 341 | "error": { 342 | gopkg: look("error").Pkg(), 343 | goobj: look("error"), 344 | gotyp: look("error").Type(), 345 | kind: skType | skBasic, 346 | goname: "error", 347 | id: "error", 348 | cpyname: "char*", 349 | cgoname: "*C.char", 350 | pysig: "str", 351 | go2py: "errorGoToPy", 352 | py2go: "errors.New(C.GoString", 353 | py2goParenEx: ")", 354 | zval: `nil`, 355 | pyfmt: "O&", 356 | }, 357 | } 358 | 359 | if reflect.TypeOf(int(0)).Size() == 8 { 360 | syms["int"] = &symbol{ 361 | gopkg: look("int").Pkg(), 362 | goobj: look("int"), 363 | gotyp: look("int").Type(), 364 | kind: skType | skBasic, 365 | goname: "int", 366 | id: "int", 367 | cpyname: "int64_t", 368 | cgoname: "C.longlong", 369 | pysig: "int", 370 | go2py: "C.longlong", 371 | py2go: "int", 372 | zval: "0", 373 | pyfmt: "k", 374 | } 375 | syms["uint"] = &symbol{ 376 | gopkg: look("uint").Pkg(), 377 | goobj: look("uint"), 378 | gotyp: look("uint").Type(), 379 | kind: skType | skBasic, 380 | goname: "uint", 381 | id: "uint", 382 | cpyname: "uint64_t", 383 | cgoname: "C.ulonglong", 384 | pysig: "int", 385 | go2py: "C.ulonglong", 386 | py2go: "uint", 387 | zval: "0", 388 | pyfmt: "K", 389 | } 390 | } 391 | 392 | // these are defined in: https://godoc.org/go/types 393 | for _, o := range []struct { 394 | kind types.BasicKind 395 | tname string 396 | uname string 397 | }{ 398 | {types.UntypedBool, "bool", "bool"}, 399 | {types.UntypedInt, "int", "int"}, 400 | {types.UntypedRune, "rune", "rune"}, 401 | {types.UntypedFloat, "float64", "float"}, 402 | {types.UntypedComplex, "complex128", "complex"}, 403 | {types.UntypedString, "string", "string"}, 404 | //FIXME(sbinet): what should be the python equivalent? 405 | //{types.UntypedNil, "nil", "nil"}, 406 | } { 407 | sym := *syms[o.tname] 408 | n := "untyped " + o.uname 409 | syms[n] = &sym 410 | } 411 | 412 | return syms 413 | } 414 | -------------------------------------------------------------------------------- /bind/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "bytes" 9 | "encoding/json" 10 | "fmt" 11 | "go/types" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "reflect" 16 | "regexp" 17 | "strconv" 18 | "strings" 19 | 20 | "github.com/pkg/errors" 21 | ) 22 | 23 | func isErrorType(typ types.Type) bool { 24 | return typ == types.Universe.Lookup("error").Type() 25 | } 26 | 27 | func isStringer(obj types.Object) bool { 28 | switch obj := obj.(type) { 29 | case *types.Func: 30 | if obj.Name() != "String" { 31 | return false 32 | } 33 | sig, ok := obj.Type().(*types.Signature) 34 | if !ok { 35 | return false 36 | } 37 | if sig.Recv() == nil { 38 | return false 39 | } 40 | if sig.Params().Len() != 0 { 41 | return false 42 | } 43 | res := sig.Results() 44 | if res.Len() != 1 { 45 | return false 46 | } 47 | ret := res.At(0).Type() 48 | if ret != types.Universe.Lookup("string").Type() { 49 | return false 50 | } 51 | return true 52 | default: 53 | return false 54 | } 55 | } 56 | 57 | func hasError(sig *types.Signature) bool { 58 | res := sig.Results() 59 | if res == nil || res.Len() <= 0 { 60 | return false 61 | } 62 | 63 | nerr := 0 64 | for i := 0; i < res.Len(); i++ { 65 | ret := res.At(i) 66 | if isErrorType(ret.Type()) { 67 | nerr++ 68 | } 69 | } 70 | 71 | switch { 72 | case nerr == 0: 73 | return false 74 | case nerr == 1: 75 | return true 76 | default: 77 | panic(fmt.Errorf( 78 | "gopy: invalid number of comma-errors (%d)", 79 | nerr, 80 | )) 81 | } 82 | } 83 | 84 | func isConstructor(sig *types.Signature) bool { 85 | //TODO(sbinet) 86 | return false 87 | } 88 | 89 | type PyConfig struct { 90 | Version int 91 | CFlags string 92 | LdFlags string 93 | LdDynamicFlags string 94 | ExtSuffix string 95 | } 96 | 97 | // AllFlags returns CFlags + " " + LdFlags 98 | func (pc *PyConfig) AllFlags() string { 99 | return strings.TrimSpace(pc.CFlags) + " " + strings.TrimSpace(pc.LdFlags) 100 | } 101 | 102 | // GetPythonConfig returns the needed python configuration for the given 103 | // python VM (python, python2, python3, pypy, etc...) 104 | func GetPythonConfig(vm string) (PyConfig, error) { 105 | code := `import sys 106 | try: 107 | import sysconfig as ds 108 | def _get_python_inc(): 109 | return ds.get_path('include') 110 | except ImportError: 111 | import distutils.sysconfig as ds 112 | _get_python_inc = ds.get_config_var 113 | import json 114 | import os 115 | version=sys.version_info.major 116 | 117 | def clear_ld_flags(s): 118 | if s is None: 119 | return '' 120 | skip_first_word = s.split(' ', 1)[1] # skip compiler name 121 | skip_bundle = skip_first_word.replace('-bundle', '') # cgo already passes -dynamiclib 122 | return skip_bundle 123 | 124 | 125 | if "GOPY_INCLUDE" in os.environ and "GOPY_LIBDIR" in os.environ and "GOPY_PYLIB" in os.environ: 126 | print(json.dumps({ 127 | "version": version, 128 | "minor": sys.version_info.minor, 129 | "incdir": os.environ["GOPY_INCLUDE"], 130 | "libdir": os.environ["GOPY_LIBDIR"], 131 | "libpy": os.environ["GOPY_PYLIB"], 132 | "shlibs": ds.get_config_var("SHLIBS"), 133 | "syslibs": ds.get_config_var("SYSLIBS"), 134 | "shlinks": ds.get_config_var("LINKFORSHARED"), 135 | "shflags": clear_ld_flags(ds.get_config_var("LDSHARED")), 136 | "extsuffix": ds.get_config_var("EXT_SUFFIX"), 137 | })) 138 | else: 139 | print(json.dumps({ 140 | "version": sys.version_info.major, 141 | "minor": sys.version_info.minor, 142 | "incdir": _get_python_inc(), 143 | "libdir": ds.get_config_var("LIBDIR"), 144 | "libpy": ds.get_config_var("LIBRARY"), 145 | "shlibs": ds.get_config_var("SHLIBS"), 146 | "syslibs": ds.get_config_var("SYSLIBS"), 147 | "shlinks": ds.get_config_var("LINKFORSHARED"), 148 | "shflags": clear_ld_flags(ds.get_config_var("LDSHARED")), 149 | "extsuffix": ds.get_config_var("EXT_SUFFIX"), 150 | })) 151 | ` 152 | 153 | var cfg PyConfig 154 | bin, err := exec.LookPath(vm) 155 | if err != nil { 156 | return cfg, errors.Wrapf(err, "could not locate python vm %q", vm) 157 | } 158 | 159 | buf := new(bytes.Buffer) 160 | cmd := exec.Command(bin, "-c", code) 161 | cmd.Stdin = os.Stdin 162 | cmd.Stdout = buf 163 | cmd.Stderr = os.Stderr 164 | err = cmd.Run() 165 | if err != nil { 166 | return cfg, errors.Wrap(err, "could not run python-config script") 167 | } 168 | 169 | var raw struct { 170 | Version int `json:"version"` 171 | Minor int `json:"minor"` 172 | IncDir string `json:"incdir"` 173 | LibDir string `json:"libdir"` 174 | LibPy string `json:"libpy"` 175 | ShLibs string `json:"shlibs"` 176 | SysLibs string `json:"syslibs"` 177 | ExtSuffix string `json:"extsuffix"` 178 | ShFlags string `json:"shflags"` 179 | } 180 | err = json.NewDecoder(buf).Decode(&raw) 181 | if err != nil { 182 | return cfg, errors.Wrapf(err, "could not decode JSON script output") 183 | } 184 | 185 | raw.IncDir = filepath.ToSlash(raw.IncDir) 186 | raw.LibDir = filepath.ToSlash(raw.LibDir) 187 | 188 | // on windows these can be empty -- use include dir which is usu good 189 | // replace suffix case insensitive 'include' with 'libs' 190 | if raw.LibDir == "" && raw.IncDir != "" { 191 | regexInc := regexp.MustCompile(`(?i)\binclude$`) 192 | raw.LibDir = regexInc.ReplaceAllString(raw.IncDir, "libs") 193 | fmt.Printf("no LibDir -- copy from IncDir: %s\n", raw.LibDir) 194 | } 195 | 196 | if raw.LibPy == "" { 197 | raw.LibPy = fmt.Sprintf("python%d%d", raw.Version, raw.Minor) 198 | fmt.Printf("no LibPy -- set to: %s\n", raw.LibPy) 199 | } 200 | 201 | raw.LibPy = strings.TrimSuffix(raw.LibPy, ".a") 202 | raw.LibPy = strings.TrimPrefix(raw.LibPy, "lib") 203 | 204 | cfg.Version = raw.Version 205 | cfg.ExtSuffix = raw.ExtSuffix 206 | cfg.CFlags = strings.Join([]string{ 207 | `"-I` + raw.IncDir + `"`, 208 | }, " ") 209 | cfg.LdFlags = strings.Join([]string{ 210 | `"-L` + raw.LibDir + `"`, 211 | `"-l` + raw.LibPy + `"`, 212 | raw.ShLibs, 213 | raw.SysLibs, 214 | }, " ") 215 | cfg.LdDynamicFlags = raw.ShFlags 216 | 217 | return cfg, nil 218 | } 219 | 220 | func getGoVersion(version string) (int64, int64, error) { 221 | version_regex := regexp.MustCompile(`^go((\d+)(\.(\d+))*)`) 222 | match := version_regex.FindStringSubmatch(version) 223 | if match == nil { 224 | return -1, -1, fmt.Errorf("gopy: invalid Go version information: %q", version) 225 | } 226 | version_info := strings.Split(match[1], ".") 227 | major, _ := strconv.ParseInt(version_info[0], 10, 0) 228 | minor, _ := strconv.ParseInt(version_info[1], 10, 0) 229 | return major, minor, nil 230 | } 231 | 232 | var ( 233 | rxValidPythonName = regexp.MustCompile(`^[\pL_][\pL_\pN]+$`) 234 | ) 235 | 236 | func extractPythonName(gname, gdoc string) (string, string, error) { 237 | const ( 238 | PythonName = "gopy:name " 239 | NLPythonName = "\n" + PythonName 240 | ) 241 | i := -1 242 | var tag string 243 | // Check for either a doc string that starts with our tag, 244 | // or as the first token of a newline 245 | if strings.HasPrefix(gdoc, PythonName) { 246 | i = 0 247 | tag = PythonName 248 | } else { 249 | i = strings.Index(gdoc, NLPythonName) 250 | tag = NLPythonName 251 | } 252 | if i < 0 { 253 | return gname, gdoc, nil 254 | } 255 | s := gdoc[i+len(tag):] 256 | if end := strings.Index(s, "\n"); end > 0 { 257 | if !isValidPythonName(s[:end]) { 258 | return "", "", fmt.Errorf("gopy: invalid identifier: %s", s[:end]) 259 | } 260 | return s[:end], gdoc[:i] + s[end:], nil 261 | } 262 | return gname, gdoc, nil 263 | } 264 | 265 | // extractPythonNameFieldTag parses a struct field tag and returns 266 | // a new python name. If the tag is not defined then the original 267 | // name is returned. 268 | // If the tag name is specified but is an invalid python identifier, 269 | // then an error is returned. 270 | func extractPythonNameFieldTag(gname, tag string) (string, error) { 271 | const tagKey = "gopy" 272 | if tag == "" { 273 | return gname, nil 274 | } 275 | tagVal := reflect.StructTag(tag).Get(tagKey) 276 | if tagVal == "" { 277 | return gname, nil 278 | } 279 | if !isValidPythonName(tagVal) { 280 | return "", fmt.Errorf("gopy: invalid identifier for struct field tag: %s", tagVal) 281 | } 282 | return tagVal, nil 283 | } 284 | 285 | // isValidPythonName returns true if the string is a valid 286 | // python identifier name 287 | func isValidPythonName(name string) bool { 288 | if name == "" { 289 | return false 290 | } 291 | return rxValidPythonName.MatchString(name) 292 | } 293 | 294 | var ( 295 | rxMatchFirstCap = regexp.MustCompile("([A-Z])([A-Z][a-z])") 296 | rxMatchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") 297 | ) 298 | 299 | // toSnakeCase converts the provided string to snake_case. 300 | // Based on https://gist.github.com/stoewer/fbe273b711e6a06315d19552dd4d33e6 301 | func toSnakeCase(input string) string { 302 | output := rxMatchFirstCap.ReplaceAllString(input, "${1}_${2}") 303 | output = rxMatchAllCap.ReplaceAllString(output, "${1}_${2}") 304 | output = strings.ReplaceAll(output, "-", "_") 305 | return strings.ToLower(output) 306 | } 307 | -------------------------------------------------------------------------------- /bind/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bind 6 | 7 | import ( 8 | "errors" 9 | "testing" 10 | ) 11 | 12 | func TestGetGoVersion(t *testing.T) { 13 | for _, tt := range []struct { 14 | info string 15 | major int64 16 | minor int64 17 | err error 18 | }{ 19 | {"go1.5", 1, 5, nil}, 20 | {"go1.6", 1, 6, nil}, 21 | {"go1.7", 1, 7, nil}, 22 | {"go1.8", 1, 8, nil}, 23 | {"gcc4", -1, -1, errors.New("gopy: invalid Go version information: \"gcc4\"")}, 24 | {"1.8go", -1, -1, errors.New("gopy: invalid Go version information: \"1.8go\"")}, 25 | {"llvm", -1, -1, errors.New("gopy: invalid Go version information: \"llvm\"")}, 26 | } { 27 | major, minor, err := getGoVersion(tt.info) 28 | if major != tt.major { 29 | t.Errorf("getGoVersion(%s): expected major %d, actual %d", tt.info, tt.major, major) 30 | } 31 | 32 | if minor != tt.minor { 33 | t.Errorf("getGoVersion(%s): expected major %d, actual %d", tt.info, tt.minor, minor) 34 | } 35 | 36 | if err != nil && err.Error() != tt.err.Error() { 37 | t.Errorf("getGoVersion(%s): expected err %s, actual %s", tt.info, tt.err, err) 38 | } 39 | } 40 | } 41 | 42 | func TestExtractPythonName(t *testing.T) { 43 | for _, tt := range []struct { 44 | name string 45 | goDoc string 46 | newName string 47 | newGoDoc string 48 | err error 49 | }{ 50 | {"Func1", "", "Func1", "", nil}, 51 | {"Func2", "\ngopy:name func2\n", "func2", "\n", nil}, 52 | {"Func3", "\ngopy:name bad name\n", "", "", errors.New("gopy: invalid identifier: bad name")}, 53 | {"Func4", "\nsome comment\n", "Func4", "\nsome comment\n", nil}, 54 | {"Func5", "\nsome comment\ngopy:name func5\n", "func5", "\nsome comment\n", nil}, 55 | {"Func6", "\nsome comment\ngopy:name __len__\n", "__len__", "\nsome comment\n", nil}, 56 | } { 57 | newName, newGoDoc, err := extractPythonName(tt.name, tt.goDoc) 58 | 59 | if newName != tt.newName { 60 | t.Errorf("extractPythonName(%s, %s): expected name %s, actual %s", tt.name, tt.goDoc, tt.newName, newName) 61 | } 62 | 63 | if newGoDoc != tt.newGoDoc { 64 | t.Errorf("extractPythonName(%s, %s): expected comment %s, actual %s", tt.name, tt.goDoc, tt.newGoDoc, newGoDoc) 65 | } 66 | 67 | if err != nil && err.Error() != tt.err.Error() { 68 | t.Errorf("extractPythonName(%s, %s): expected err %s, actual %s", tt.name, tt.goDoc, tt.err, err) 69 | } 70 | } 71 | } 72 | 73 | func TestPythonConfig(t *testing.T) { 74 | t.Skip() 75 | 76 | for _, tc := range []struct { 77 | vm string 78 | want PyConfig 79 | }{ 80 | { 81 | vm: "python2", 82 | }, 83 | { 84 | vm: "python3", 85 | }, 86 | } { 87 | t.Run(tc.vm, func(t *testing.T) { 88 | cfg, err := GetPythonConfig(tc.vm) 89 | if err != nil { 90 | t.Fatal(err) 91 | } 92 | if cfg != tc.want { 93 | t.Fatalf("error:\ngot= %#v\nwant=%#v\n", cfg, tc.want) 94 | } 95 | }) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /cmd_build.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "bytes" 9 | "fmt" 10 | "log" 11 | "os" 12 | "os/exec" 13 | "path/filepath" 14 | "runtime" 15 | "strings" 16 | 17 | "github.com/gonuts/commander" 18 | "github.com/gonuts/flag" 19 | 20 | "github.com/go-python/gopy/bind" 21 | ) 22 | 23 | func gopyMakeCmdBuild() *commander.Command { 24 | cmd := &commander.Command{ 25 | Run: gopyRunCmdBuild, 26 | UsageLine: "build [other-go-package...]", 27 | Short: "generate and compile (C)Python language bindings for Go", 28 | Long: ` 29 | build generates and compiles (C)Python language bindings for Go package(s). 30 | 31 | ex: 32 | $ gopy build [options] [other-go-package...] 33 | $ gopy build github.com/go-python/gopy/_examples/hi 34 | `, 35 | Flag: *flag.NewFlagSet("gopy-build", flag.ExitOnError), 36 | } 37 | 38 | cmd.Flag.String("vm", "python", "path to python interpreter") 39 | cmd.Flag.String("output", "", "output directory for bindings") 40 | cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)") 41 | cmd.Flag.String("main", "", "code string to run in the go main() function in the cgo library") 42 | cmd.Flag.String("package-prefix", ".", "custom package prefix used when generating import "+ 43 | "statements for generated package") 44 | cmd.Flag.Bool("rename", false, "rename Go symbols to python PEP snake_case") 45 | cmd.Flag.Bool("symbols", true, "include symbols in output") 46 | cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected") 47 | cmd.Flag.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile") 48 | cmd.Flag.Bool("dynamic-link", false, "whether to link output shared library dynamically to Python") 49 | cmd.Flag.String("build-tags", "", "build tags to be passed to `go build`") 50 | return cmd 51 | } 52 | 53 | func gopyRunCmdBuild(cmdr *commander.Command, args []string) error { 54 | if len(args) == 0 { 55 | err := fmt.Errorf("gopy: expect a fully qualified go package name as argument") 56 | log.Println(err) 57 | return err 58 | } 59 | 60 | cfg := NewBuildCfg() 61 | cfg.OutputDir = cmdr.Flag.Lookup("output").Value.Get().(string) 62 | cfg.Name = cmdr.Flag.Lookup("name").Value.Get().(string) 63 | cfg.Main = cmdr.Flag.Lookup("main").Value.Get().(string) 64 | cfg.VM = cmdr.Flag.Lookup("vm").Value.Get().(string) 65 | cfg.PkgPrefix = cmdr.Flag.Lookup("package-prefix").Value.Get().(string) 66 | cfg.RenameCase = cmdr.Flag.Lookup("rename").Value.Get().(bool) 67 | cfg.Symbols = cmdr.Flag.Lookup("symbols").Value.Get().(bool) 68 | cfg.NoWarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool) 69 | cfg.NoMake = cmdr.Flag.Lookup("no-make").Value.Get().(bool) 70 | cfg.DynamicLinking = cmdr.Flag.Lookup("dynamic-link").Value.Get().(bool) 71 | cfg.BuildTags = cmdr.Flag.Lookup("build-tags").Value.Get().(string) 72 | 73 | bind.NoWarn = cfg.NoWarn 74 | bind.NoMake = cfg.NoMake 75 | 76 | for _, path := range args { 77 | bpkg, err := loadPackage(path, true, cfg.BuildTags) // build first 78 | if err != nil { 79 | return fmt.Errorf("gopy-gen: go build / load of package failed with path=%q: %v", path, err) 80 | } 81 | pkg, err := parsePackage(bpkg) 82 | if err != nil { 83 | return err 84 | } 85 | if cfg.Name == "" { 86 | cfg.Name = pkg.Name() 87 | } 88 | } 89 | return runBuild("build", cfg) 90 | } 91 | 92 | // runBuild calls genPkg and then executes commands to build the resulting files 93 | // exe = executable mode to build an executable instead of a library 94 | // mode = gen, build, pkg, exe 95 | func runBuild(mode bind.BuildMode, cfg *BuildCfg) error { 96 | var err error 97 | cfg.OutputDir, err = genOutDir(cfg.OutputDir) 98 | if err != nil { 99 | return err 100 | } 101 | err = genPkg(mode, cfg) 102 | if err != nil { 103 | return err 104 | } 105 | 106 | fmt.Printf("\n--- building package ---\n%s\n", cfg.Cmd) 107 | 108 | buildname := cfg.Name + "_go" 109 | var cmdout []byte 110 | cwd, err := os.Getwd() 111 | os.Chdir(cfg.OutputDir) 112 | defer os.Chdir(cwd) 113 | 114 | os.Remove(cfg.Name + ".c") // may fail, we don't care 115 | 116 | fmt.Printf("goimports -w %v\n", cfg.Name+".go") 117 | cmd := exec.Command("goimports", "-w", cfg.Name+".go") 118 | cmdout, err = cmd.CombinedOutput() 119 | if err != nil { 120 | fmt.Printf("cmd had error: %v output:\no%v\n", err, string(cmdout)) 121 | return err 122 | } 123 | 124 | pycfg, err := bind.GetPythonConfig(cfg.VM) 125 | 126 | if mode == bind.ModeExe { 127 | of, err := os.Create(buildname + ".h") // overwrite existing 128 | fmt.Fprintf(of, "typedef uint8_t bool;\n") 129 | of.Close() 130 | 131 | fmt.Printf("%v build.py # will fail, but needed to generate .c file\n", cfg.VM) 132 | cmd = exec.Command(cfg.VM, "build.py") 133 | cmd.Run() // will fail, we don't care about errors 134 | 135 | args := []string{"build", "-mod=mod", "-buildmode=c-shared"} 136 | if cfg.BuildTags != "" { 137 | args = append(args, "-tags", cfg.BuildTags) 138 | } 139 | args = append(args, "-o", buildname+libExt, ".") 140 | 141 | fmt.Printf("go %v\n", strings.Join(args, " ")) 142 | cmd = exec.Command("go", args...) 143 | cmdout, err = cmd.CombinedOutput() 144 | if err != nil { 145 | fmt.Printf("cmd had error: %v output:\n%v\n", err, string(cmdout)) 146 | return err 147 | } 148 | 149 | fmt.Printf("%v build.py # should work this time\n", cfg.VM) 150 | cmd = exec.Command(cfg.VM, "build.py") 151 | cmdout, err = cmd.CombinedOutput() 152 | if err != nil { 153 | fmt.Printf("cmd had error: %v output:\n%v\n", err, string(cmdout)) 154 | return err 155 | } 156 | 157 | err = os.Remove(cfg.Name + "_go" + libExt) 158 | 159 | fmt.Printf("go build -o py%s\n", cfg.Name) 160 | cmd = exec.Command("go", "build", "-mod=mod") 161 | if cfg.BuildTags != "" { 162 | args = append(args, "-tags", cfg.BuildTags) 163 | } 164 | args = append(args, "-o", "py"+cfg.Name) 165 | cmdout, err = cmd.CombinedOutput() 166 | if err != nil { 167 | fmt.Printf("cmd had error: %v output:\n%v\n", err, string(cmdout)) 168 | return err 169 | } 170 | 171 | } else { 172 | buildLib := buildname + libExt 173 | extext := libExt 174 | if runtime.GOOS == "windows" { 175 | extext = ".pyd" 176 | } 177 | if pycfg.ExtSuffix != "" { 178 | extext = pycfg.ExtSuffix 179 | } 180 | modlib := "_" + cfg.Name + extext 181 | 182 | // build the go shared library upfront to generate the header 183 | // needed by our generated cpython code 184 | args := []string{"build", "-mod=mod", "-buildmode=c-shared"} 185 | if cfg.BuildTags != "" { 186 | args = append(args, "-tags", cfg.BuildTags) 187 | } 188 | if !cfg.Symbols { 189 | // These flags will omit the various symbol tables, thereby 190 | // reducing the final size of the binary. From https://golang.org/cmd/link/ 191 | // -s Omit the symbol table and debug information 192 | // -w Omit the DWARF symbol table 193 | args = append(args, "-ldflags=-s -w") 194 | } 195 | args = append(args, "-o", buildLib, ".") 196 | fmt.Printf("go %v\n", strings.Join(args, " ")) 197 | cmd = exec.Command("go", args...) 198 | cmdout, err = cmd.CombinedOutput() 199 | if err != nil { 200 | fmt.Printf("cmd had error: %v output:\n%v\n", err, string(cmdout)) 201 | return err 202 | } 203 | // update the output name to the one with the ABI extension 204 | args[len(args)-2] = modlib 205 | // we don't need this initial lib because we are going to relink 206 | os.Remove(buildLib) 207 | 208 | // generate c code 209 | fmt.Printf("%v build.py\n", cfg.VM) 210 | cmd = exec.Command(cfg.VM, "build.py") 211 | cmdout, err = cmd.CombinedOutput() 212 | if err != nil { 213 | fmt.Printf("cmd had error: %v output:\no%v\n", err, string(cmdout)) 214 | return err 215 | } 216 | 217 | if bind.WindowsOS { 218 | fmt.Printf("Doing windows sed hack to fix declspec for PyInit\n") 219 | fname := cfg.Name + ".c" 220 | raw, err := os.ReadFile(fname) 221 | if err != nil { 222 | fmt.Printf("could not read %s: %+v", fname, err) 223 | return fmt.Errorf("could not read %s: %w", fname, err) 224 | } 225 | raw = bytes.ReplaceAll(raw, []byte(" PyInit_"), []byte(" __declspec(dllexport) PyInit_")) 226 | err = os.WriteFile(fname, raw, 0644) 227 | if err != nil { 228 | fmt.Printf("could not apply sed hack to fix declspec for PyInit: %+v", err) 229 | return fmt.Errorf("could not apply sed hack to fix PyInit: %w", err) 230 | } 231 | } 232 | 233 | cflags := strings.Fields(strings.TrimSpace(pycfg.CFlags)) 234 | cflags = append(cflags, "-fPIC", "-Ofast") 235 | if include, exists := os.LookupEnv("GOPY_INCLUDE"); exists { 236 | cflags = append(cflags, "-I"+filepath.ToSlash(include)) 237 | } 238 | if oldcflags, exists := os.LookupEnv("CGO_CFLAGS"); exists { 239 | cflags = append(cflags, oldcflags) 240 | } 241 | var ldflags []string 242 | if cfg.DynamicLinking { 243 | ldflags = strings.Fields(strings.TrimSpace(pycfg.LdDynamicFlags)) 244 | } else { 245 | ldflags = strings.Fields(strings.TrimSpace(pycfg.LdFlags)) 246 | } 247 | if !cfg.Symbols { 248 | ldflags = append(ldflags, "-s") 249 | } 250 | if lib, exists := os.LookupEnv("GOPY_LIBDIR"); exists { 251 | ldflags = append(ldflags, "-L"+filepath.ToSlash(lib)) 252 | } 253 | if libname, exists := os.LookupEnv("GOPY_PYLIB"); exists { 254 | ldflags = append(ldflags, "-l"+filepath.ToSlash(libname)) 255 | } 256 | if oldldflags, exists := os.LookupEnv("CGO_LDFLAGS"); exists { 257 | ldflags = append(ldflags, oldldflags) 258 | } 259 | 260 | removeEmpty := func(src []string) []string { 261 | o := make([]string, 0, len(src)) 262 | for _, v := range src { 263 | if v == "" { 264 | continue 265 | } 266 | o = append(o, v) 267 | } 268 | return o 269 | } 270 | 271 | cflags = removeEmpty(cflags) 272 | ldflags = removeEmpty(ldflags) 273 | 274 | cflagsEnv := fmt.Sprintf("CGO_CFLAGS=%s", strings.Join(cflags, " ")) 275 | ldflagsEnv := fmt.Sprintf("CGO_LDFLAGS=%s", strings.Join(ldflags, " ")) 276 | 277 | env := os.Environ() 278 | env = append(env, cflagsEnv) 279 | env = append(env, ldflagsEnv) 280 | 281 | fmt.Println(cflagsEnv) 282 | fmt.Println(ldflagsEnv) 283 | 284 | // build extension with go + c 285 | fmt.Printf("go %v\n", strings.Join(args, " ")) 286 | cmd = exec.Command("go", args...) 287 | cmd.Env = env 288 | cmdout, err = cmd.CombinedOutput() 289 | if err != nil { 290 | fmt.Printf("cmd had error: %v output:\n%v\n", err, string(cmdout)) 291 | return err 292 | } 293 | } 294 | 295 | return err 296 | } 297 | -------------------------------------------------------------------------------- /cmd_exe.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | 14 | "github.com/go-python/gopy/bind" 15 | "github.com/gonuts/commander" 16 | "github.com/gonuts/flag" 17 | ) 18 | 19 | // python packaging links: 20 | // https://pypi.org/ 21 | // https://packaging.python.org/tutorials/packaging-projects/ 22 | // https://docs.python.org/3/tutorial/modules.html 23 | 24 | func gopyMakeCmdExe() *commander.Command { 25 | cmd := &commander.Command{ 26 | Run: gopyRunCmdExe, 27 | UsageLine: "exe [other-go-package...]", 28 | Short: "generate and compile (C)Python language bindings for Go, and make a standalone python executable with all the code -- must provide suitable main function code", 29 | Long: ` 30 | exe generates and compiles (C)Python language bindings for a Go package, including subdirectories, and generates a standalone python executable and associated module packaging suitable for distribution. if setup.py file does not yet exist in the target directory, then it along with other default packaging files are created, using arguments. Typically you create initial default versions of these files and then edit them, and after that, only regenerate the go binding files. 31 | 32 | The primary need for an exe instead of a pkg dynamic library is when the main thread must be used for something other than running the python interpreter, such as for a GUI library where the main thread must be used for running the GUI event loop (e.g., GoGi). 33 | 34 | When including multiple packages, list in order of increasing dependency, and use -name arg to give appropriate name. 35 | 36 | ex: 37 | $ gopy exe [options] [other-go-package...] 38 | $ gopy exe github.com/go-python/gopy/_examples/hi 39 | `, 40 | Flag: *flag.NewFlagSet("gopy-exe", flag.ExitOnError), 41 | } 42 | 43 | cmd.Flag.String("vm", "python", "path to python interpreter") 44 | cmd.Flag.String("output", "", "output directory for root of package") 45 | cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)") 46 | cmd.Flag.String("main", "", "code string to run in the go main() function in the cgo library "+ 47 | "-- defaults to GoPyMainRun() but typically should be overriden") 48 | // cmd.Flag.String("package-prefix", ".", "custom package prefix used when generating import "+ 49 | // "statements for generated package") 50 | cmd.Flag.Bool("rename", false, "rename Go symbols to python PEP snake_case") 51 | cmd.Flag.Bool("symbols", true, "include symbols in output") 52 | cmd.Flag.String("exclude", "", "comma-separated list of package names to exclude") 53 | cmd.Flag.String("user", "", "username on https://www.pypa.io/en/latest/ for package name suffix") 54 | cmd.Flag.String("version", "0.1.0", "semantic version number -- can use e.g., git to get this from tag and pass as argument") 55 | cmd.Flag.String("author", "gopy", "author name") 56 | cmd.Flag.String("email", "gopy@example.com", "author email") 57 | cmd.Flag.String("desc", "", "short description of project (long comes from README.md)") 58 | cmd.Flag.String("url", "https://github.com/go-python/gopy", "home page for project") 59 | cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected") 60 | cmd.Flag.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile") 61 | cmd.Flag.Bool("dynamic-link", false, "whether to link output shared library dynamically to Python") 62 | cmd.Flag.String("build-tags", "", "build tags to be passed to `go build`") 63 | 64 | return cmd 65 | } 66 | 67 | func gopyRunCmdExe(cmdr *commander.Command, args []string) error { 68 | if len(args) == 0 { 69 | err := fmt.Errorf("gopy: expect a fully qualified go package name as argument") 70 | log.Println(err) 71 | return err 72 | } 73 | 74 | cfg := NewBuildCfg() 75 | cfg.OutputDir = cmdr.Flag.Lookup("output").Value.Get().(string) 76 | cfg.Name = cmdr.Flag.Lookup("name").Value.Get().(string) 77 | cfg.Main = cmdr.Flag.Lookup("main").Value.Get().(string) 78 | cfg.VM = cmdr.Flag.Lookup("vm").Value.Get().(string) 79 | // cfg.PkgPrefix = cmdr.Flag.Lookup("package-prefix").Value.Get().(string) 80 | cfg.PkgPrefix = "" // doesn't make sense for exe 81 | cfg.RenameCase = cmdr.Flag.Lookup("rename").Value.Get().(bool) 82 | cfg.Symbols = cmdr.Flag.Lookup("symbols").Value.Get().(bool) 83 | cfg.NoWarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool) 84 | cfg.NoMake = cmdr.Flag.Lookup("no-make").Value.Get().(bool) 85 | cfg.DynamicLinking = cmdr.Flag.Lookup("dynamic-link").Value.Get().(bool) 86 | cfg.BuildTags = cmdr.Flag.Lookup("build-tags").Value.Get().(string) 87 | 88 | var ( 89 | exclude = cmdr.Flag.Lookup("exclude").Value.Get().(string) 90 | user = cmdr.Flag.Lookup("user").Value.Get().(string) 91 | version = cmdr.Flag.Lookup("version").Value.Get().(string) 92 | author = cmdr.Flag.Lookup("author").Value.Get().(string) 93 | email = cmdr.Flag.Lookup("email").Value.Get().(string) 94 | desc = cmdr.Flag.Lookup("desc").Value.Get().(string) 95 | url = cmdr.Flag.Lookup("url").Value.Get().(string) 96 | ) 97 | 98 | bind.NoWarn = cfg.NoWarn 99 | bind.NoMake = cfg.NoMake 100 | 101 | if cfg.Name == "" { 102 | path := args[0] 103 | _, cfg.Name = filepath.Split(path) 104 | } 105 | 106 | var err error 107 | cfg.OutputDir, err = genOutDir(cfg.OutputDir) 108 | if err != nil { 109 | return err 110 | } 111 | 112 | setupfn := filepath.Join(cfg.OutputDir, "setup.py") 113 | 114 | if _, err = os.Stat(setupfn); os.IsNotExist(err) { 115 | err = GenPyPkgSetup(cfg, user, version, author, email, desc, url) 116 | if err != nil { 117 | return err 118 | } 119 | } 120 | 121 | defex := []string{"testdata", "internal", "python", "examples", "cmd"} 122 | excl := append(strings.Split(exclude, ","), defex...) 123 | exmap := make(map[string]struct{}) 124 | for i := range excl { 125 | ex := strings.TrimSpace(excl[i]) 126 | exmap[ex] = struct{}{} 127 | } 128 | 129 | cfg.OutputDir = filepath.Join(cfg.OutputDir, cfg.Name) // package must be in subdir 130 | cfg.OutputDir, err = genOutDir(cfg.OutputDir) 131 | if err != nil { 132 | return err 133 | } 134 | 135 | for _, path := range args { 136 | buildPkgRecurse(cfg.OutputDir, path, path, exmap, cfg.BuildTags) 137 | } 138 | return runBuild(bind.ModeExe, cfg) 139 | } 140 | -------------------------------------------------------------------------------- /cmd_gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | 11 | "github.com/go-python/gopy/bind" 12 | "github.com/gonuts/commander" 13 | "github.com/gonuts/flag" 14 | ) 15 | 16 | func gopyMakeCmdGen() *commander.Command { 17 | cmd := &commander.Command{ 18 | Run: gopyRunCmdGen, 19 | UsageLine: "gen [other-go-package...]", 20 | Short: "generate (C)Python language bindings for Go", 21 | Long: ` 22 | gen generates (C)Python language bindings for Go package(s). 23 | 24 | ex: 25 | $ gopy gen [options] [other-go-package...] 26 | $ gopy gen github.com/go-python/gopy/_examples/hi 27 | `, 28 | Flag: *flag.NewFlagSet("gopy-gen", flag.ExitOnError), 29 | } 30 | 31 | cmd.Flag.String("vm", "python", "path to python interpreter") 32 | cmd.Flag.String("output", "", "output directory for bindings") 33 | cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)") 34 | cmd.Flag.String("main", "", "code string to run in the go main() function in the cgo library") 35 | cmd.Flag.String("package-prefix", ".", "custom package prefix used when generating import "+ 36 | "statements for generated package") 37 | cmd.Flag.Bool("rename", false, "rename Go symbols to python PEP snake_case") 38 | cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected") 39 | cmd.Flag.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile") 40 | cmd.Flag.Bool("dynamic-link", false, "whether to link output shared library dynamically to Python") 41 | cmd.Flag.String("build-tags", "", "build tags to be passed to `go build`") 42 | return cmd 43 | } 44 | 45 | func gopyRunCmdGen(cmdr *commander.Command, args []string) error { 46 | var err error 47 | 48 | if len(args) == 0 { 49 | err := fmt.Errorf("gopy: expect a fully qualified go package name as argument") 50 | log.Println(err) 51 | return err 52 | } 53 | 54 | cfg := NewBuildCfg() 55 | cfg.OutputDir = cmdr.Flag.Lookup("output").Value.Get().(string) 56 | cfg.VM = cmdr.Flag.Lookup("vm").Value.Get().(string) 57 | cfg.Name = cmdr.Flag.Lookup("name").Value.Get().(string) 58 | cfg.Main = cmdr.Flag.Lookup("main").Value.Get().(string) 59 | cfg.PkgPrefix = cmdr.Flag.Lookup("package-prefix").Value.Get().(string) 60 | cfg.RenameCase = cmdr.Flag.Lookup("rename").Value.Get().(bool) 61 | cfg.NoWarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool) 62 | cfg.NoMake = cmdr.Flag.Lookup("no-make").Value.Get().(bool) 63 | cfg.DynamicLinking = cmdr.Flag.Lookup("dynamic-link").Value.Get().(bool) 64 | cfg.BuildTags = cmdr.Flag.Lookup("build-tags").Value.Get().(string) 65 | 66 | if cfg.VM == "" { 67 | cfg.VM = "python" 68 | } 69 | 70 | bind.NoWarn = cfg.NoWarn 71 | bind.NoMake = cfg.NoMake 72 | 73 | for _, path := range args { 74 | bpkg, err := loadPackage(path, true, cfg.BuildTags) // build first 75 | if err != nil { 76 | return fmt.Errorf("gopy-gen: go build / load of package failed with path=%q: %v", path, err) 77 | } 78 | pkg, err := parsePackage(bpkg) 79 | if cfg.Name == "" { 80 | cfg.Name = pkg.Name() 81 | } 82 | if err != nil { 83 | return err 84 | } 85 | } 86 | 87 | err = genPkg(bind.ModeGen, cfg) 88 | if err != nil { 89 | return err 90 | } 91 | 92 | return err 93 | } 94 | -------------------------------------------------------------------------------- /cmd_pkg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "log" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | 14 | "github.com/go-python/gopy/bind" 15 | "github.com/gonuts/commander" 16 | "github.com/gonuts/flag" 17 | ) 18 | 19 | // python packaging links: 20 | // https://pypi.org/ 21 | // https://packaging.python.org/tutorials/packaging-projects/ 22 | // https://docs.python.org/3/tutorial/modules.html 23 | 24 | func gopyMakeCmdPkg() *commander.Command { 25 | cmd := &commander.Command{ 26 | Run: gopyRunCmdPkg, 27 | UsageLine: "pkg [other-go-package...]", 28 | Short: "generate and compile (C)Python language bindings for Go, and make a python package", 29 | Long: ` 30 | pkg generates and compiles (C)Python language bindings for a Go package, including subdirectories, and generates python module packaging suitable for distribution. if setup.py file does not yet exist in the target directory, then it along with other default packaging files are created, using arguments. Typically you create initial default versions of these files and then edit them, and after that, only regenerate the go binding files. 31 | 32 | When including multiple packages, list in order of increasing dependency, and use -name arg to give appropriate name. 33 | 34 | ex: 35 | $ gopy pkg [options] [other-go-package...] 36 | $ gopy pkg github.com/go-python/gopy/_examples/hi 37 | `, 38 | Flag: *flag.NewFlagSet("gopy-pkg", flag.ExitOnError), 39 | } 40 | 41 | cmd.Flag.String("vm", "python", "path to python interpreter") 42 | cmd.Flag.String("output", "", "output directory for root of package") 43 | cmd.Flag.String("name", "", "name of output package (otherwise name of first package is used)") 44 | cmd.Flag.String("main", "", "code string to run in the go GoPyInit() function in the cgo library") 45 | cmd.Flag.String("package-prefix", ".", "custom package prefix used when generating import "+ 46 | "statements for generated package") 47 | cmd.Flag.Bool("rename", false, "rename Go symbols to python PEP snake_case") 48 | cmd.Flag.Bool("symbols", true, "include symbols in output") 49 | cmd.Flag.String("exclude", "", "comma-separated list of package names to exclude") 50 | cmd.Flag.String("user", "", "username on https://www.pypa.io/en/latest/ for package name suffix") 51 | cmd.Flag.String("version", "0.1.0", "semantic version number -- can use e.g., git to get this from tag and pass as argument") 52 | cmd.Flag.String("author", "gopy", "author name") 53 | cmd.Flag.String("email", "gopy@example.com", "author email") 54 | cmd.Flag.String("desc", "", "short description of project (long comes from README.md)") 55 | cmd.Flag.String("url", "https://github.com/go-python/gopy", "home page for project") 56 | cmd.Flag.Bool("no-warn", false, "suppress warning messages, which may be expected") 57 | cmd.Flag.Bool("no-make", false, "do not generate a Makefile, e.g., when called from Makefile") 58 | cmd.Flag.Bool("dynamic-link", false, "whether to link output shared library dynamically to Python") 59 | cmd.Flag.String("build-tags", "", "build tags to be passed to `go build`") 60 | 61 | return cmd 62 | } 63 | 64 | func gopyRunCmdPkg(cmdr *commander.Command, args []string) error { 65 | if len(args) == 0 { 66 | err := fmt.Errorf("gopy: expect a fully qualified go package name as argument") 67 | log.Println(err) 68 | return err 69 | } 70 | 71 | cfg := NewBuildCfg() 72 | cfg.OutputDir = cmdr.Flag.Lookup("output").Value.Get().(string) 73 | cfg.Name = cmdr.Flag.Lookup("name").Value.Get().(string) 74 | cfg.Main = cmdr.Flag.Lookup("main").Value.Get().(string) 75 | cfg.VM = cmdr.Flag.Lookup("vm").Value.Get().(string) 76 | cfg.PkgPrefix = cmdr.Flag.Lookup("package-prefix").Value.Get().(string) 77 | cfg.RenameCase = cmdr.Flag.Lookup("rename").Value.Get().(bool) 78 | cfg.Symbols = cmdr.Flag.Lookup("symbols").Value.Get().(bool) 79 | cfg.NoWarn = cmdr.Flag.Lookup("no-warn").Value.Get().(bool) 80 | cfg.NoMake = cmdr.Flag.Lookup("no-make").Value.Get().(bool) 81 | cfg.DynamicLinking = cmdr.Flag.Lookup("dynamic-link").Value.Get().(bool) 82 | cfg.BuildTags = cmdr.Flag.Lookup("build-tags").Value.Get().(string) 83 | 84 | var ( 85 | exclude = cmdr.Flag.Lookup("exclude").Value.Get().(string) 86 | user = cmdr.Flag.Lookup("user").Value.Get().(string) 87 | version = cmdr.Flag.Lookup("version").Value.Get().(string) 88 | author = cmdr.Flag.Lookup("author").Value.Get().(string) 89 | email = cmdr.Flag.Lookup("email").Value.Get().(string) 90 | desc = cmdr.Flag.Lookup("desc").Value.Get().(string) 91 | url = cmdr.Flag.Lookup("url").Value.Get().(string) 92 | ) 93 | 94 | bind.NoWarn = cfg.NoWarn 95 | bind.NoMake = cfg.NoMake 96 | 97 | if cfg.Name == "" { 98 | path := args[0] 99 | _, cfg.Name = filepath.Split(path) 100 | } 101 | 102 | var err error 103 | cfg.OutputDir, err = genOutDir(cfg.OutputDir) 104 | if err != nil { 105 | return err 106 | } 107 | 108 | setupfn := filepath.Join(cfg.OutputDir, "setup.py") 109 | 110 | if _, err = os.Stat(setupfn); os.IsNotExist(err) { 111 | err = GenPyPkgSetup(cfg, user, version, author, email, desc, url) 112 | if err != nil { 113 | return err 114 | } 115 | } 116 | 117 | defex := []string{"testdata", "internal", "python", "examples", "cmd"} 118 | excl := append(strings.Split(exclude, ","), defex...) 119 | exmap := make(map[string]struct{}) 120 | for i := range excl { 121 | ex := strings.TrimSpace(excl[i]) 122 | exmap[ex] = struct{}{} 123 | } 124 | 125 | cfg.OutputDir = filepath.Join(cfg.OutputDir, cfg.Name) // package must be in subdir 126 | cfg.OutputDir, err = genOutDir(cfg.OutputDir) 127 | if err != nil { 128 | return err 129 | } 130 | 131 | for _, path := range args { 132 | buildPkgRecurse(cfg.OutputDir, path, path, exmap, cfg.BuildTags) 133 | } 134 | return runBuild(bind.ModePkg, cfg) 135 | } 136 | 137 | func buildPkgRecurse(odir, path, rootpath string, exmap map[string]struct{}, buildTags string) error { 138 | buildFirst := path == rootpath 139 | bpkg, err := loadPackage(path, buildFirst, buildTags) 140 | if err != nil { 141 | return fmt.Errorf("gopy-gen: go build / load of package failed with path=%q: %v", path, err) 142 | } 143 | gofiles := bpkg.GoFiles 144 | onego := "" 145 | if len(gofiles) == 1 { 146 | _, onego = filepath.Split(gofiles[0]) 147 | } 148 | if len(gofiles) == 0 || (len(gofiles) == 1 && onego == "doc.go") { 149 | fmt.Printf("\n--- skipping dir with no go files or only doc.go: %s -- %s\n", path, gofiles) 150 | if len(gofiles) == 0 { 151 | // fmt.Printf("otherfiles: %v\nignorefiles: %v\n", bpkg.OtherFiles, bpkg.IgnoredFiles) 152 | if len(bpkg.OtherFiles) > 0 { 153 | gofiles = bpkg.OtherFiles 154 | } else if len(bpkg.IgnoredFiles) > 0 { 155 | gofiles = bpkg.IgnoredFiles 156 | } else { 157 | return nil // done 158 | } 159 | } 160 | } else { 161 | // fmt.Printf("gofiles: %s\n", gofiles) 162 | parsePackage(bpkg) 163 | } 164 | 165 | // now try all subdirs 166 | dir, _ := filepath.Split(gofiles[0]) 167 | drs := Dirs(dir) 168 | for _, dr := range drs { 169 | _, ex := exmap[dr] 170 | if ex || dr[0] == '.' || dr[0] == '_' || dr == "internal" { 171 | continue 172 | } 173 | sp := filepath.Join(path, dr) 174 | buildPkgRecurse(odir, sp, rootpath, exmap, buildTags) 175 | } 176 | return nil 177 | } 178 | -------------------------------------------------------------------------------- /dirs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "os" 9 | ) 10 | 11 | // Dirs returns a slice of all the directories within a given directory 12 | func Dirs(path string) []string { 13 | files, err := os.ReadDir(path) 14 | if err != nil { 15 | return nil 16 | } 17 | 18 | var fnms []string 19 | for _, fi := range files { 20 | if fi.IsDir() { 21 | fnms = append(fnms, fi.Name()) 22 | } 23 | } 24 | return fnms 25 | } 26 | -------------------------------------------------------------------------------- /doc.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | /* 6 | gopy generates (and compiles) language bindings that make it possible to call Go code 7 | and pass objects from Python. 8 | 9 | # Using gopy 10 | 11 | gopy takes a Go package and generates bindings for all of the exported 12 | symbols. The exported symbols define the cross-language interface. 13 | 14 | The gopy tool generates both an API stub in Python, and binding code in 15 | Go. Start with a Go package: 16 | 17 | package hi 18 | 19 | import "fmt" 20 | 21 | func Hello(name string) { 22 | fmt.Println("Hello, %s!\n", name) 23 | } 24 | */ 25 | package main 26 | -------------------------------------------------------------------------------- /gen.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "go/ast" 10 | "go/doc" 11 | "go/parser" 12 | "go/token" 13 | "log" 14 | "os" 15 | "os/exec" 16 | "path/filepath" 17 | "strings" 18 | 19 | "github.com/pkg/errors" 20 | "golang.org/x/tools/go/packages" 21 | 22 | "github.com/go-python/gopy/bind" 23 | ) 24 | 25 | // argStr returns the full command args as a string, without path to exe 26 | func argStr() string { 27 | ma := make([]string, len(os.Args)) 28 | copy(ma, os.Args) 29 | _, cmd := filepath.Split(ma[0]) 30 | ma[0] = cmd 31 | for i := range ma { 32 | if strings.HasPrefix(ma[i], "-main=") { 33 | ma[i] = "-main=\"" + ma[i][6:] + "\"" 34 | } 35 | } 36 | return strings.Join(ma, " ") 37 | } 38 | 39 | // genOutDir makes the output directory and returns its absolute path 40 | func genOutDir(odir string) (string, error) { 41 | cwd, err := os.Getwd() 42 | if err != nil { 43 | log.Fatal(err) 44 | } 45 | 46 | if odir == "" { 47 | odir = cwd 48 | } else { 49 | err = os.MkdirAll(odir, 0755) 50 | if err != nil { 51 | return odir, fmt.Errorf("gopy-gen: could not create output directory: %v", err) 52 | } 53 | } 54 | odir, err = filepath.Abs(odir) 55 | if err != nil { 56 | return odir, fmt.Errorf("gopy-gen: could not infer absolute path to output directory: %v", err) 57 | } 58 | return odir, nil 59 | } 60 | 61 | // genPkg generates output for all the current packages that have been parsed, 62 | // in the given output directory 63 | // mode = gen, build, pkg, exe 64 | func genPkg(mode bind.BuildMode, cfg *BuildCfg) error { 65 | var err error 66 | cfg.OutputDir, err = genOutDir(cfg.OutputDir) 67 | if err != nil { 68 | return err 69 | } 70 | if !filepath.IsAbs(cfg.VM) { 71 | cfg.VM, err = exec.LookPath(cfg.VM) 72 | if err != nil { 73 | return errors.Wrapf(err, "could not locate absolute path to python VM") 74 | } 75 | } 76 | 77 | pyvers, err := getPythonVersion(cfg.VM) 78 | if err != nil { 79 | return err 80 | } 81 | err = bind.GenPyBind(mode, libExt, extraGccArgs, pyvers, cfg.DynamicLinking, &cfg.BindCfg) 82 | if err != nil { 83 | log.Println(err) 84 | } 85 | return err 86 | } 87 | 88 | func loadPackage(path string, buildFirst bool, buildTags string) (*packages.Package, error) { 89 | cwd, err := os.Getwd() 90 | if err != nil { 91 | return nil, err 92 | } 93 | 94 | if buildFirst { 95 | args := []string{"build"} 96 | if buildTags != "" { 97 | buildTagStr := fmt.Sprintf("\"%s\"", strings.Join(strings.Split(buildTags, ","), " ")) 98 | args = append(args, "-tags", buildTagStr) 99 | } 100 | args = append(args, "-v", path) 101 | fmt.Printf("go %s\n", strings.Join(args, " ")) 102 | cmd := exec.Command("go", args...) 103 | cmd.Stdin = os.Stdin 104 | cmd.Stdout = os.Stdout 105 | cmd.Stderr = os.Stderr 106 | cmd.Dir = cwd 107 | 108 | err = cmd.Run() 109 | if err != nil { 110 | log.Printf("Note: there was an error building [%s] -- will continue but it may fail later: %v\n", 111 | path, 112 | err, 113 | ) 114 | } 115 | } 116 | 117 | // golang.org/x/tools/go/packages supports modules or GOPATH etc 118 | mode := packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedDeps | packages.NeedImports | packages.NeedTypes | packages.NeedTypesSizes 119 | bpkgs, err := packages.Load(&packages.Config{Mode: mode}, path) 120 | if err != nil { 121 | log.Printf("error resolving import path [%s]: %v\n", 122 | path, 123 | err, 124 | ) 125 | return nil, err 126 | } 127 | 128 | bpkg := bpkgs[0] // only ever have one at a time 129 | return bpkg, nil 130 | } 131 | 132 | func parsePackage(bpkg *packages.Package) (*bind.Package, error) { 133 | if len(bpkg.GoFiles) == 0 { 134 | err := fmt.Errorf("gopy: no files in package %q", bpkg.PkgPath) 135 | fmt.Println(err) 136 | return nil, err 137 | } 138 | dir, _ := filepath.Split(bpkg.GoFiles[0]) 139 | p := bpkg.Types 140 | 141 | if bpkg.Name == "main" { 142 | err := fmt.Errorf("gopy: skipping 'main' package %q", bpkg.PkgPath) 143 | fmt.Println(err) 144 | return nil, err 145 | } 146 | 147 | fset := token.NewFileSet() 148 | var pkgast *ast.Package 149 | pkgs, err := parser.ParseDir(fset, dir, nil, parser.ParseComments) 150 | if err != nil { 151 | return nil, err 152 | } 153 | pkgast = pkgs[p.Name()] 154 | if pkgast == nil { 155 | return nil, fmt.Errorf("gopy: could not find AST for package %q", p.Name()) 156 | } 157 | 158 | pkgdoc := doc.New(pkgast, bpkg.PkgPath, 0) 159 | return bind.NewPackage(p, pkgdoc) 160 | } 161 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/go-python/gopy 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/gonuts/commander v0.1.0 7 | github.com/gonuts/flag v0.1.0 8 | github.com/pkg/errors v0.9.1 9 | golang.org/x/tools v0.16.0 10 | ) 11 | 12 | require golang.org/x/mod v0.14.0 // indirect 13 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gonuts/commander v0.1.0 h1:EcDTiVw9oAVORFjQOEOuHQqcl6OXMyTgELocTq6zJ0I= 2 | github.com/gonuts/commander v0.1.0/go.mod h1:qkb5mSlcWodYgo7vs8ulLnXhfinhZsZcm6+H/z1JjgY= 3 | github.com/gonuts/flag v0.1.0 h1:fqMv/MZ+oNGu0i9gp0/IQ/ZaPIDoAZBOBaJoV7viCWM= 4 | github.com/gonuts/flag v0.1.0/go.mod h1:ZTmTGtrSPejTo/SRNhCqwLTmiAgyBdCkLYhHrAoBdz4= 5 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 6 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 7 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 8 | golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= 9 | golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 10 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 11 | golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= 12 | golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 13 | golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= 14 | golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 15 | -------------------------------------------------------------------------------- /gopy.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 The go-python Authors. All rights reserved. 2 | # Use of this source code is governed by a BSD-style 3 | # license that can be found in the LICENSE file. 4 | 5 | ### py2/py3 compat 6 | from __future__ import print_function 7 | 8 | __doc__ = """gopy is a convenience module to wrap and bind a Go package""" 9 | __author__ = "The go-python authors" 10 | 11 | __all__ = [ 12 | "load", 13 | ] 14 | 15 | ### stdlib imports --- 16 | import imp 17 | import os 18 | import sys 19 | 20 | _PY3 = sys.version_info[0] == 3 21 | 22 | def load(pkg, output="", capi="cpython"): 23 | """ 24 | `load` takes a fully qualified Go package name and runs `gopy bind` on it. 25 | @returns the C-extension module object 26 | """ 27 | if _PY3: capi = "cffi" 28 | 29 | from subprocess import check_call, check_output 30 | if output == "": 31 | output = os.getcwd() 32 | pass 33 | 34 | print("gopy> inferring package name...") 35 | pkg = check_output(["go", "list", pkg]).strip() 36 | if _PY3: 37 | pkg = pkg.decode("utf-8") 38 | if pkg in sys.modules: 39 | print("gopy> package '%s' already wrapped and loaded!" % (pkg,)) 40 | print("gopy> NOT recompiling it again (see issue #27)") 41 | return sys.modules[pkg] 42 | print("gopy> loading '%s'..." % pkg) 43 | 44 | check_call(["gopy", "bind", "-vm=%s" % sys.executable, "-api=%s" % capi, "-output=%s" % output, pkg]) 45 | 46 | n = os.path.basename(pkg) 47 | print("gopy> importing '%s'" % (pkg,)) 48 | 49 | ok = imp.find_module(n, [output]) 50 | if not ok: 51 | raise RuntimeError("could not find module '%s'" % pkg) 52 | fname, path, descr = ok 53 | mod = imp.load_module('__gopy__.'+n, fname, path, descr) 54 | mod.__name__ = pkg 55 | sys.modules[pkg] = mod 56 | del sys.modules['__gopy__.'+n] 57 | return mod 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /gopyh/handle.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | // Package gopyh provides the variable handle manager for gopy. 6 | // The handles map can NOT be globally shared in C because it 7 | // must use interface{} values that can change location via GC. 8 | // In effect, each gopy package must be thought of as a completely 9 | // separate Go instance, and there can be NO sharing of anything 10 | // between them, because they fundamentally live in different .so 11 | // libraries. 12 | // Thus, we must ensure that all handles used within a package 13 | // are registered within that same package -- this means python 14 | // users typically will import a single package, which exports 15 | // all the relevant functionality as needed. Any further packages 16 | // cannot share anything other than basic types (int, string etc). 17 | package gopyh 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "reflect" 23 | "strconv" 24 | "sync" 25 | ) 26 | 27 | // GoHandle is the type for the handle 28 | type GoHandle int64 29 | type CGoHandle int64 30 | 31 | // --- variable handles: all pointers managed via handles --- 32 | 33 | var ( 34 | mu sync.RWMutex 35 | ctr int64 36 | handles map[GoHandle]interface{} 37 | counts map[GoHandle]int64 38 | ) 39 | 40 | // IfaceIsNil returns true if interface or value represented by interface is nil 41 | func IfaceIsNil(it interface{}) bool { 42 | if it == nil { 43 | return true 44 | } 45 | v := reflect.ValueOf(it) 46 | vk := v.Kind() 47 | if vk == reflect.Ptr || vk == reflect.Interface || vk == reflect.Map || vk == reflect.Slice || vk == reflect.Func || vk == reflect.Chan { 48 | return v.IsNil() 49 | } 50 | return false 51 | } 52 | 53 | // NonPtrValue returns the non-pointer underlying value 54 | func NonPtrValue(v reflect.Value) reflect.Value { 55 | for v.Kind() == reflect.Ptr { 56 | v = v.Elem() 57 | } 58 | return v 59 | } 60 | 61 | // PtrValue returns the pointer version (Addr()) of the underlying value if 62 | // the value is not already a Ptr 63 | func PtrValue(v reflect.Value) reflect.Value { 64 | if v.CanAddr() && v.Kind() != reflect.Ptr { 65 | v = v.Addr() 66 | } 67 | return v 68 | } 69 | 70 | // Embed returns the embedded struct (in first field only) of given type within given struct 71 | func Embed(stru interface{}, embed reflect.Type) interface{} { 72 | if IfaceIsNil(stru) { 73 | return nil 74 | } 75 | v := NonPtrValue(reflect.ValueOf(stru)) 76 | typ := v.Type() 77 | if typ == embed { 78 | return PtrValue(v).Interface() 79 | } 80 | if typ.NumField() == 0 { 81 | return nil 82 | } 83 | f := typ.Field(0) 84 | if f.Type.Kind() == reflect.Struct && f.Anonymous { // anon only avail on StructField fm typ 85 | vf := v.Field(0) 86 | vfpi := PtrValue(vf).Interface() 87 | if f.Type == embed { 88 | return vfpi 89 | } 90 | rv := Embed(vfpi, embed) 91 | if rv != nil { 92 | return rv 93 | } 94 | } 95 | return nil 96 | } 97 | 98 | var ( 99 | trace = false 100 | ) 101 | 102 | func init() { 103 | if len(os.Getenv("GOPY_HANDLE_TRACE")) > 0 { 104 | trace = true 105 | } 106 | } 107 | 108 | // Register registers a new variable instance. 109 | func Register(typnm string, ifc interface{}) CGoHandle { 110 | if IfaceIsNil(ifc) { 111 | return -1 112 | } 113 | mu.Lock() 114 | defer mu.Unlock() 115 | if handles == nil { 116 | handles = make(map[GoHandle]interface{}) 117 | counts = make(map[GoHandle]int64) 118 | } 119 | ctr++ 120 | hc := ctr 121 | ghc := GoHandle(hc) 122 | handles[ghc] = ifc 123 | counts[ghc] = 0 124 | if trace { 125 | fmt.Printf("gopy Registered: %s %v %d\n", typnm, ifc, hc) 126 | } 127 | return CGoHandle(hc) 128 | } 129 | 130 | // DecRef decrements the reference count for the specified handle 131 | // and removes it if the reference count goes to zero. 132 | func DecRef(handle CGoHandle) { 133 | if handle < 1 { 134 | return 135 | } 136 | mu.Lock() 137 | defer mu.Unlock() 138 | if handles == nil { 139 | return 140 | } 141 | ghc := GoHandle(handle) 142 | if _, exists := handles[ghc]; !exists { 143 | return 144 | } 145 | counts[ghc]-- 146 | switch cnt := counts[ghc]; { 147 | case cnt == 0: 148 | delete(counts, ghc) 149 | delete(handles, ghc) 150 | if trace { 151 | fmt.Printf("gopy DecRef: %d\n", handle) 152 | } 153 | case cnt < 0: 154 | panic(fmt.Sprintf("gopy DecRef ref count %v for handle: %v, ifc %v", cnt, ghc, handles[ghc])) 155 | default: 156 | if trace { 157 | fmt.Printf("gopy DecRef: %d: %d\n", handle, cnt) 158 | } 159 | } 160 | } 161 | 162 | // IncRef increments the reference count for the specified handle. 163 | func IncRef(handle CGoHandle) { 164 | if handle < 1 { 165 | return 166 | } 167 | mu.Lock() 168 | defer mu.Unlock() 169 | ghc := GoHandle(handle) 170 | if _, exists := counts[ghc]; exists { 171 | counts[ghc]++ 172 | if trace { 173 | fmt.Printf("gopy IncRef: %d: %d\n", handle, counts[ghc]) 174 | } 175 | } 176 | 177 | } 178 | 179 | // VarFromHandle gets variable from handle string. 180 | // Reports error to python but does not return it, 181 | // for use in inline calls 182 | func VarFromHandle(h CGoHandle, typnm string) interface{} { 183 | v, _ := VarFromHandleTry(h, typnm) 184 | return v 185 | } 186 | 187 | // VarFromHandleTry version returns the error explicitly, 188 | // for use when error can be processed 189 | func VarFromHandleTry(h CGoHandle, typnm string) (interface{}, error) { 190 | if h < 1 { 191 | return nil, fmt.Errorf("gopy: nil handle") 192 | } 193 | mu.RLock() 194 | defer mu.RUnlock() 195 | v, has := handles[GoHandle(h)] 196 | if !has { 197 | err := fmt.Errorf("gopy: variable handle not registered: " + strconv.FormatInt(int64(h), 10)) 198 | // TODO: need to get access to this: 199 | // C.PyErr_SetString(C.PyExc_TypeError, C.CString(err.Error())) 200 | return nil, err 201 | } 202 | return v, nil 203 | } 204 | 205 | // NumHandles returns the number of handles in use. 206 | func NumHandles() int { 207 | mu.RLock() 208 | defer mu.RUnlock() 209 | return len(handles) 210 | } 211 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "io" 10 | "log" 11 | "os" 12 | "path" 13 | 14 | "github.com/gonuts/commander" 15 | "github.com/gonuts/flag" 16 | "github.com/pkg/errors" 17 | 18 | "github.com/go-python/gopy/bind" 19 | ) 20 | 21 | // BuildCfg contains command options and binding generation options 22 | type BuildCfg struct { 23 | bind.BindCfg 24 | 25 | // include symbols in output 26 | Symbols bool 27 | // suppress warning messages, which may be expected 28 | NoWarn bool 29 | // do not generate a Makefile, e.g., when called from Makefile 30 | NoMake bool 31 | // link resulting library dynamically 32 | DynamicLinking bool 33 | // BuildTags to be passed into `go build`. 34 | BuildTags string 35 | } 36 | 37 | // NewBuildCfg returns a newly constructed build config 38 | func NewBuildCfg() *BuildCfg { 39 | var cfg BuildCfg 40 | cfg.Cmd = argStr() 41 | return &cfg 42 | } 43 | 44 | func run(args []string) error { 45 | app := &commander.Command{ 46 | UsageLine: "gopy", 47 | Subcommands: []*commander.Command{ 48 | gopyMakeCmdGen(), 49 | gopyMakeCmdBuild(), 50 | gopyMakeCmdPkg(), 51 | gopyMakeCmdExe(), 52 | }, 53 | Flag: *flag.NewFlagSet("gopy", flag.ExitOnError), 54 | } 55 | 56 | err := app.Flag.Parse(args) 57 | if err != nil { 58 | return fmt.Errorf("could not parse flags: %v", err) 59 | } 60 | 61 | appArgs := app.Flag.Args() 62 | err = app.Dispatch(appArgs) 63 | if err != nil { 64 | return fmt.Errorf("error dispatching command: %v", err) 65 | } 66 | return nil 67 | } 68 | 69 | func main() { 70 | err := run(os.Args[1:]) 71 | if err != nil { 72 | log.Fatal(err) 73 | } 74 | os.Exit(0) 75 | } 76 | 77 | func copyCmd(src, dst string) error { 78 | srcf, err := os.Open(src) 79 | if err != nil { 80 | return errors.Wrap(err, "could not open source for copy") 81 | } 82 | defer srcf.Close() 83 | 84 | os.MkdirAll(path.Dir(dst), 0755) 85 | 86 | dstf, err := os.Create(dst) 87 | if err != nil { 88 | return errors.Wrap(err, "could not create destination for copy") 89 | } 90 | defer dstf.Close() 91 | 92 | _, err = io.Copy(dstf, srcf) 93 | if err != nil { 94 | return errors.Wrap(err, "could not copy bytes to destination") 95 | } 96 | 97 | err = dstf.Sync() 98 | if err != nil { 99 | return errors.Wrap(err, "could not synchronize destination") 100 | } 101 | 102 | return dstf.Close() 103 | } 104 | -------------------------------------------------------------------------------- /main_darwin.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build darwin 6 | // +build darwin 7 | 8 | package main 9 | 10 | const ( 11 | // libExt = ".dylib" // theoretically should be this but python only recognizes .so 12 | libExt = ".so" 13 | extraGccArgs = "-dynamiclib" 14 | ) 15 | -------------------------------------------------------------------------------- /main_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build (linux && !android) || dragonfly || openbsd 6 | // +build linux,!android dragonfly openbsd 7 | 8 | package main 9 | 10 | const ( 11 | libExt = ".so" 12 | extraGccArgs = "" 13 | ) 14 | -------------------------------------------------------------------------------- /main_unix_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !windows 6 | // +build !windows 7 | 8 | package main 9 | 10 | import ( 11 | "log" 12 | "os" 13 | "os/exec" 14 | "strings" 15 | ) 16 | 17 | func init() { 18 | 19 | testEnvironment = os.Environ() 20 | 21 | var ( 22 | py3 = "python3" 23 | // pypy3 = "pypy3" 24 | ) 25 | 26 | if os.Getenv("GOPY_TRAVIS_CI") == "1" { 27 | log.Printf("Running in travis CI") 28 | } 29 | 30 | var ( 31 | disabled []string 32 | missing int 33 | ) 34 | for _, be := range []struct { 35 | name string 36 | vm string 37 | module string 38 | mandatory bool 39 | }{ 40 | {"py3", py3, "", true}, 41 | } { 42 | args := []string{"-c", ""} 43 | if be.module != "" { 44 | args[1] = "import " + be.module 45 | } 46 | log.Printf("checking testbackend: %q...", be.name) 47 | cmd := exec.Command(be.vm, args...) 48 | cmd.Stdin = os.Stdin 49 | cmd.Stdout = os.Stdout 50 | cmd.Stderr = os.Stderr 51 | err := cmd.Run() 52 | if err != nil { 53 | log.Printf("disabling testbackend: %q, error: '%s'", be.name, err.Error()) 54 | testBackends[be.name] = "" 55 | disabled = append(disabled, be.name) 56 | if be.mandatory { 57 | missing++ 58 | } 59 | } else { 60 | log.Printf("enabling testbackend: %q", be.name) 61 | testBackends[be.name] = be.vm 62 | } 63 | } 64 | 65 | if len(disabled) > 0 { 66 | log.Printf("The following test backends are not available: %s", 67 | strings.Join(disabled, ", ")) 68 | if os.Getenv("GOPY_TRAVIS_CI") == "1" && missing > 0 { 69 | log.Fatalf("Not all backends available in travis CI") 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /main_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build windows 6 | // +build windows 7 | 8 | package main 9 | 10 | import "github.com/go-python/gopy/bind" 11 | 12 | const ( 13 | libExt = ".pyd" 14 | extraGccArgs = "" 15 | ) 16 | 17 | func init() { 18 | bind.WindowsOS = true 19 | } 20 | -------------------------------------------------------------------------------- /main_windows_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build windows 6 | // +build windows 7 | 8 | package main 9 | 10 | import ( 11 | "log" 12 | "os" 13 | "os/exec" 14 | "strings" 15 | ) 16 | 17 | func init() { 18 | 19 | testEnvironment = os.Environ() 20 | 21 | var ( 22 | // py2 = "python2" 23 | py3 = "python3" 24 | // pypy2 = "pypy" 25 | // pypy3 = "pypy3" 26 | ) 27 | 28 | if os.Getenv("GOPY_TRAVIS_CI") == "1" { 29 | log.Printf("Running in travis CI") 30 | } 31 | 32 | var ( 33 | disabled []string 34 | missing int 35 | ) 36 | for _, be := range []struct { 37 | name string 38 | vm string 39 | module string 40 | mandatory bool 41 | }{ 42 | {"py3", py3, "", true}, 43 | } { 44 | args := []string{"-c", ""} 45 | if be.module != "" { 46 | args[1] = "import " + be.module 47 | } 48 | log.Printf("checking testbackend: %q...", be.name) 49 | 50 | py, err := exec.LookPath(be.vm) 51 | if err != nil { 52 | log.Printf("gopy: could not locate 'python' executable (err: %v)", err) 53 | } else { 54 | log.Printf("python executable found at: %s\n", py) 55 | cmd := exec.Command(py, args...) 56 | cmd.Stdin = os.Stdin 57 | cmd.Stdout = os.Stdout 58 | cmd.Stderr = os.Stderr 59 | err = cmd.Run() 60 | } 61 | if err != nil { 62 | log.Printf("disabling testbackend: %q, error: '%s'", be.name, err.Error()) 63 | testBackends[be.name] = "" 64 | disabled = append(disabled, be.name) 65 | if be.mandatory { 66 | missing++ 67 | } 68 | } else { 69 | log.Printf("enabling testbackend: %q", be.name) 70 | testBackends[be.name] = be.vm 71 | } 72 | } 73 | 74 | if len(disabled) > 0 { 75 | log.Printf("The following test backends are not available: %s", 76 | strings.Join(disabled, ", ")) 77 | if os.Getenv("GOPY_TRAVIS_CI") == "1" && missing > 0 { 78 | log.Fatalf("Not all backends available in travis CI") 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /pkgsetup.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | "path/filepath" 11 | 12 | "github.com/go-python/gopy/bind" 13 | ) 14 | 15 | // 1 = pkg name, 2 = -user, 3 = version 4 = author, 5 = email, 6 = desc, 7 = url 16 | const ( 17 | setupTempl = `import setuptools 18 | 19 | with open("README.md", "r") as fh: 20 | long_description = fh.read() 21 | 22 | 23 | class BinaryDistribution(setuptools.Distribution): 24 | def has_ext_modules(_): 25 | return True 26 | 27 | 28 | setuptools.setup( 29 | name="%[1]s%[2]s", 30 | version="%[3]s", 31 | author="%[4]s", 32 | author_email="%[5]s", 33 | description="%[6]s", 34 | long_description=long_description, 35 | long_description_content_type="text/markdown", 36 | url="%[7]s", 37 | packages=setuptools.find_packages(), 38 | classifiers=[ 39 | "Programming Language :: Python :: 3", 40 | "License :: OSI Approved :: BSD License", 41 | "Operating System :: OS Independent", 42 | ], 43 | include_package_data=True, 44 | distclass=BinaryDistribution, 45 | ) 46 | ` 47 | 48 | manifestTempl = `global-include *.so *.py *.pyd 49 | global-exclude build.py 50 | ` 51 | 52 | // 1 = pkg name 53 | bsdLicense = `BSD 3-Clause License 54 | 55 | Copyright (c) 2018, The %[1]s Authors 56 | All rights reserved. 57 | 58 | Redistribution and use in source and binary forms, with or without 59 | modification, are permitted provided that the following conditions are met: 60 | 61 | * Redistributions of source code must retain the above copyright notice, this 62 | list of conditions and the following disclaimer. 63 | 64 | * Redistributions in binary form must reproduce the above copyright notice, 65 | this list of conditions and the following disclaimer in the documentation 66 | and/or other materials provided with the distribution. 67 | 68 | * Neither the name of the copyright holder nor the names of its 69 | contributors may be used to endorse or promote products derived from 70 | this software without specific prior written permission. 71 | 72 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 73 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 74 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 75 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 76 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 77 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 78 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 79 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 80 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 81 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 82 | ` 83 | 84 | // 1 = pkg name, 2 = desc 85 | readmeTempl = `# %[1]s 86 | 87 | %[2]s 88 | 89 | ` 90 | 91 | // 1 = pkg name, 2 = cmd, 3 = gencmd, 4 = vm (exe only) 92 | makefileTempl = `# Makefile for gopy pkg generation of python bindings to %[1]s 93 | # File is generated by gopy (will not be overwritten though) 94 | # %[2]s 95 | 96 | PYTHON=%[4]s 97 | PIP=$(PYTHON) -m pip 98 | 99 | all: gen 100 | 101 | install: install-pkg install-exe 102 | 103 | gen: 104 | %[3]s 105 | 106 | build: 107 | $(MAKE) -C %[1]s build 108 | 109 | install-pkg: 110 | # this does a local install of the package, building the sdist and then directly installing it 111 | rm -rf dist build */*.egg-info *.egg-info 112 | $(PYTHON) setup.py sdist 113 | $(PIP) install dist/*.tar.gz 114 | 115 | install-exe: 116 | # install executable into /usr/local/bin 117 | cp %[1]s/py%[1]s /usr/local/bin/ 118 | 119 | ` 120 | ) 121 | 122 | // GenPyPkgSetup generates python package setup files 123 | func GenPyPkgSetup(cfg *BuildCfg, user, version, author, email, desc, url string) error { 124 | os.Chdir(cfg.OutputDir) 125 | 126 | dashUser := user 127 | if user != "" { 128 | dashUser = "-" + user 129 | } 130 | 131 | sf, err := os.Create(filepath.Join(cfg.OutputDir, "setup.py")) 132 | if err != nil { 133 | return err 134 | } 135 | fmt.Fprintf(sf, setupTempl, cfg.Name, dashUser, version, author, email, desc, url) 136 | sf.Close() 137 | 138 | mi, err := os.Create(filepath.Join(cfg.OutputDir, "MANIFEST.in")) 139 | if err != nil { 140 | return err 141 | } 142 | fmt.Fprintf(mi, manifestTempl) 143 | mi.Close() 144 | 145 | lf, err := os.Create(filepath.Join(cfg.OutputDir, "LICENSE")) 146 | if err != nil { 147 | return err 148 | } 149 | fmt.Fprintf(lf, bsdLicense, cfg.Name) 150 | lf.Close() 151 | 152 | rf, err := os.Create(filepath.Join(cfg.OutputDir, "README.md")) 153 | if err != nil { 154 | return err 155 | } 156 | fmt.Fprintf(rf, readmeTempl, cfg.Name, desc) 157 | rf.Close() 158 | 159 | _, pyonly := filepath.Split(cfg.VM) 160 | gencmd := bind.CmdStrToMakefile(cfg.Cmd) 161 | 162 | mf, err := os.Create(filepath.Join(cfg.OutputDir, "Makefile")) 163 | if err != nil { 164 | return err 165 | } 166 | fmt.Fprintf(mf, makefileTempl, cfg.Name, cfg.Cmd, gencmd, pyonly) 167 | mf.Close() 168 | 169 | return err 170 | } 171 | -------------------------------------------------------------------------------- /python.go: -------------------------------------------------------------------------------- 1 | // Copyright 2015 The go-python Authors. All rights reserved. 2 | // Use of this source code is governed by a BSD-style 3 | // license that can be found in the LICENSE file. 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "os/exec" 10 | "strconv" 11 | "strings" 12 | 13 | "github.com/pkg/errors" 14 | ) 15 | 16 | // getPythonVersion returns the python version available on this machine 17 | func getPythonVersion(vm string) (int, error) { 18 | py, err := exec.LookPath(vm) 19 | if err != nil { 20 | return 0, fmt.Errorf( 21 | "gopy: could not locate 'python' executable (err: %v)", 22 | err, 23 | ) 24 | } 25 | 26 | out, err := exec.Command(py, "-c", "import sys; print(sys.version_info.major)").Output() 27 | if err != nil { 28 | return 0, errors.Wrapf(err, "gopy: error retrieving python version") 29 | } 30 | 31 | vers, err := strconv.Atoi(strings.TrimSpace(string(out))) 32 | if err != nil { 33 | return 0, errors.Wrapf(err, "gopy: error retrieving python version") 34 | } 35 | 36 | return vers, nil 37 | } 38 | -------------------------------------------------------------------------------- /version.go: -------------------------------------------------------------------------------- 1 | // WARNING: auto-generated by Makefile release target -- run 'make release' to update 2 | 3 | package main 4 | 5 | const ( 6 | Version = "v0.4.10" 7 | GitCommit = "b735a58" // the commit JUST BEFORE the release 8 | VersionDate = "2024-05-03 22:57" // UTC 9 | ) 10 | --------------------------------------------------------------------------------