Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jobs:
fail-fast: false
matrix:
go-version: [1.25.x, 1.24.x, 1.22.x, 1.21.x]
platform: [ubuntu-latest, windows-latest]
platform: [ubuntu-latest, windows-latest, macos-13]
#platform: [ubuntu-latest, macos-latest, windows-latest]
runs-on: ${{ matrix.platform }}
steps:
Expand Down Expand Up @@ -59,6 +59,13 @@ jobs:
# install goimports
go install golang.org/x/tools/cmd/goimports@latest

- name: Install macOS packages
if: matrix.platform == 'macos-13'
run: |
# install pybindgen
python3 -m pip install --user -U pybindgen
# install goimports
go install golang.org/x/tools/cmd/goimports@latest

- name: Build-Linux
if: matrix.platform == 'ubuntu-latest'
Expand All @@ -77,6 +84,15 @@ jobs:
if: matrix.platform == 'windows-latest'
run: |
go test -v ./...

- name: Build-macOS
if: matrix.platform == 'macos-13'
run: |
make
- name: Test macOS
if: matrix.platform == 'macos-13'
run: |
make test
- name: Upload-Coverage
if: matrix.platform == 'ubuntu-latest'
uses: codecov/codecov-action@v4
1 change: 1 addition & 0 deletions SUPPORT_MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ _examples/consts | yes
_examples/cstrings | yes
_examples/empty | yes
_examples/funcs | yes
_examples/gilstring | yes
_examples/gobytes | yes
_examples/gopygc | yes
_examples/gostrings | yes
Expand Down
2 changes: 1 addition & 1 deletion _examples/cgo/cgo.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ package cgo
//#include <string.h>
//#include <stdlib.h>
//const char* cpkg_sprintf(const char *str) {
// char *o = (char*)malloc(strlen(str));
// char *o = (char*)malloc(strlen(str) + 1);
// sprintf(o, "%s", str);
// return o;
//}
Expand Down
8 changes: 8 additions & 0 deletions _examples/cgo/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,12 @@
print("cgo.Hi()= %s" % repr(cgo.Hi()).lstrip('u'))
print("cgo.Hello(you)= %s" % repr(cgo.Hello("you")).lstrip('u'))

# Regression test for GIL ordering bug (issue #370 / PR #386):
# go functions returning string (go2py=C.CString) previously called C.CString
# without holding the GIL, causing crashes under repeated calls.
for _ in range(5000):
assert cgo.Hi() == 'hi from go\n'
assert cgo.Hello("world") == 'hello world from go\n'
print("stress OK")

print("OK")
38 changes: 38 additions & 0 deletions _examples/gilstring/gilstring.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright 2026 The go-python Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package gilstring is a regression test for the GIL ordering bug (issue #370).
// It exercises go2py string conversion through functions, struct fields,
// slice elements, and map values — all of which previously ran C.CString
// without holding the GIL, causing crashes under repeated calls.
package gilstring

import "fmt"

// Add returns the sum of its arguments, mirroring simple.Add from the issue
// report reproduction script.
func Add(i, j int) int { return i + j }

// Hello returns a greeting string, mirroring hi.Hello from the issue report
// reproduction script. Returning a non-trivial string stresses go2py
// string conversion (C.CString) on every call.
func Hello(s string) string { return fmt.Sprintf("Hello, %s!", s) }

// Item is a struct with a string field to exercise struct member string getters.
type Item struct {
Label string
Count int
}

// MakeItem returns an Item with the given label.
func MakeItem(s string) Item { return Item{Label: s, Count: len(s)} }

// GetLabel returns the Label field of an Item.
func GetLabel(i Item) string { return i.Label }

// StringSlice returns a slice of strings.
func StringSlice() []string { return []string{"alpha", "beta", "gamma"} }

// StringMap returns a map with string values.
func StringMap() map[string]string { return map[string]string{"key": "value"} }
37 changes: 37 additions & 0 deletions _examples/gilstring/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Copyright 2026 The go-python Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.

## py2/py3 compat
from __future__ import print_function

import gilstring

# Regression test for GIL ordering bug (issue #370 / PR #386).
# Mirrors the exact reproduction script from the issue report:
# for _ in range(5000):
# Add(2, 2)
# Hello('hi')
# Integer arithmetic (Add) interleaved with string-returning calls (Hello)
# stresses the go2py C.CString path that previously ran without the GIL held.
N = 5000

for _ in range(N):
assert gilstring.Add(2, 2) == 4
assert gilstring.Hello("hi") == "Hello, hi!"

# Struct string fields, slice elements, and map values exercise additional
# go2py string conversion paths from the same bug.
for _ in range(N):
item = gilstring.MakeItem("hello")
assert item.Label == "hello", item.Label

for _ in range(N):
s = gilstring.StringSlice()
assert s[0] == "alpha", s[0]

for _ in range(N):
m = gilstring.StringMap()
assert m["key"] == "value", m["key"]

print("OK")
16 changes: 14 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ var (
"_examples/cstrings": []string{"py3"},
"_examples/pkgconflict": []string{"py3"},
"_examples/variadic": []string{"py3"},
"_examples/gilstring": []string{"py3"},
}

testEnvironment = os.Environ()
Expand Down Expand Up @@ -316,7 +317,6 @@ OK
}

func TestBindSimple(t *testing.T) {
t.Skip("Skipping due to Go 1.21+ CGO issue (see https://github.com/go-python/gopy/issues/370)")
// t.Parallel()
path := "_examples/simple"
testPkg(t, pkg{
Expand Down Expand Up @@ -546,7 +546,6 @@ OK
}

func TestBindCgoPackage(t *testing.T) {
t.Skip("Skipping due to Go 1.21+ CGO issue (see https://github.com/go-python/gopy/issues/370)")
// t.Parallel()
path := "_examples/cgo"
testPkg(t, pkg{
Expand All @@ -557,6 +556,7 @@ func TestBindCgoPackage(t *testing.T) {
want: []byte(`cgo.doc: '\nPackage cgo tests bindings of CGo-based packages.\n\n'
cgo.Hi()= 'hi from go\n'
cgo.Hello(you)= 'hello you from go\n'
stress OK
OK
`),
})
Expand Down Expand Up @@ -785,6 +785,18 @@ OK
})
}

func TestGilString(t *testing.T) {
// t.Parallel()
path := "_examples/gilstring"
testPkg(t, pkg{
path: path,
lang: features[path],
cmd: "build",
extras: nil,
want: []byte("OK\n"),
})
}

func TestPackagePrefix(t *testing.T) {
// t.Parallel()
path := "_examples/package/mypkg"
Expand Down
Loading