Documentation
¶
Overview ¶
Package pyffi provides Go bindings for the CPython C-API using purego. No Cgo is required — the CPython shared library is loaded at runtime.
Quick Start ¶
rt, err := pyffi.New()
if err != nil {
log.Fatal(err)
}
defer rt.Close()
rt.Exec(`x = 1 + 2`)
result, _ := rt.Eval("x * 10")
defer result.Close()
val, _ := result.Int64() // 30
Import and Call ¶
math, _ := rt.Import("math")
defer math.Close()
r, _ := math.Attr("sqrt").Call(16.0)
defer r.Close()
f, _ := r.Float64() // 4.0
Collections ¶
list, _ := rt.NewList(1, 2, 3)
dict, _ := rt.NewDict("name", "Go", "year", 2009)
tuple, _ := rt.NewTuple("a", "b", "c")
set, _ := rt.NewSet(1, 2, 3)
item, _ := list.GetItem(0) // list[0]
list.SetItem(0, 99) // list[0] = 99
list.DelItem(0) // del list[0]
ok, _ := list.Contains(2) // 2 in list
n, _ := list.Len() // len(list)
Type Conversions ¶
The following Go ↔ Python type mappings are automatic:
Go type Python type bool bool int, int8–64 int uint, uint8–64 int float32, float64 float string str []byte bytes []any list map[string]any dict nil None
In the reverse direction:
Python type Go type bool bool int int64 float float64 str string bytes, bytearray []byte list, tuple, set []any dict map[string]any None nil
Goroutine Safety (Auto-GIL) ¶
All public methods automatically acquire and release the Python GIL. You can safely call any method from any goroutine without manual GIL management:
go func() {
result, _ := rt.Eval("1 + 1")
defer result.Close()
// Safe — GIL is acquired automatically.
}()
For batching multiple operations under a single GIL acquisition (reducing overhead), use Runtime.WithGIL:
rt.WithGIL(func() error {
rt.Exec("a = 1")
rt.Exec("b = 2")
rt.Exec("c = a + b")
return nil
})
Async Python ¶
result, _ := rt.RunAsync("some_async_func()")
result, _ := rt.Eval("asyncio.run(coro)")
// Non-blocking (runs in a background goroutine):
ch := rt.RunAsyncGo("fetch_data(url)")
ar := <-ch // AsyncResult{Value, Err}
Callbacks (Go → Python → Go) ¶
rt.RegisterFunc("add", func(a, b int) int { return a + b })
rt.Exec(`
import go_bridge
result = go_bridge.add(1, 2) // calls Go function
`)
Free-Threaded Python ¶
Python 3.13t+ (free-threaded builds) are automatically detected. When running on a free-threaded build, runtime.LockOSThread calls are skipped since there is no GIL to contend for.
Example ¶
if sharedRT == nil {
fmt.Println(30)
return
}
rt := sharedRT
rt.Exec(`x = 1 + 2`)
result, _ := rt.Eval("x * 10")
defer result.Close()
val, _ := result.Int64()
fmt.Println(val)
Output: 30
Example (Collections) ¶
if sharedRT == nil {
fmt.Println("20\nGo")
return
}
rt := sharedRT
list, _ := rt.NewList(10, 20, 30)
defer list.Close()
item, _ := list.GetItem(1)
defer item.Close()
val, _ := item.Int64()
fmt.Println(val)
dict, _ := rt.NewDict("name", "Go", "year", 2009)
defer dict.Close()
name, _ := dict.GetItem("name")
defer name.Close()
s, _ := name.GoString()
fmt.Println(s)
Output: 20 Go
Example (Import) ¶
if sharedRT == nil {
fmt.Println("3.1416")
return
}
rt := sharedRT
math, _ := rt.Import("math")
defer math.Close()
pi := math.Attr("pi")
defer pi.Close()
val, _ := pi.Float64()
fmt.Printf("%.4f\n", val)
Output: 3.1416
Index ¶
- Constants
- Variables
- func ClearCache() error
- func UVAvailable() bool
- func UVVersion() (string, error)
- type AsyncResult
- type BoundModule
- func (m *BoundModule) Attr(name string) *Object
- func (m *BoundModule) Call(name string, args ...any) (*Object, error)
- func (m *BoundModule) CallKw(name string, args []any, kwargs map[string]any) (*Object, error)
- func (m *BoundModule) Close() error
- func (m *BoundModule) Has(name string) bool
- func (m *BoundModule) Names() ([]string, error)
- type EventLoop
- type GILState
- type Iterator
- type KW
- type LibraryNotFoundError
- type Object
- func (o *Object) Attr(name string) *Object
- func (o *Object) AttrErr(name string) (*Object, error)
- func (o *Object) Bool() (bool, error)
- func (o *Object) Call(args ...any) (*Object, error)
- func (o *Object) CallAsync(args ...any) (*Object, error)
- func (o *Object) CallAsyncGo(args ...any) <-chan AsyncResult
- func (o *Object) CallKw(args []any, kwargs map[string]any) (*Object, error)
- func (o *Object) Close() error
- func (o *Object) Compare(other *Object, op int) (bool, error)
- func (o *Object) Contains(item any) (bool, error)
- func (o *Object) DelItem(key any) error
- func (o *Object) Enter() (*Object, error)
- func (o *Object) Equals(other *Object) (bool, error)
- func (o *Object) Exit(err error) error
- func (o *Object) Float64() (float64, error)
- func (o *Object) GetItem(key any) (*Object, error)
- func (o *Object) GoBytes() ([]byte, error)
- func (o *Object) GoMap() (map[string]any, error)
- func (o *Object) GoSlice() ([]any, error)
- func (o *Object) GoString() (string, error)
- func (o *Object) GoValue() (any, error)
- func (o *Object) Int64() (int64, error)
- func (o *Object) IsNone() bool
- func (o *Object) Iter() (*Iterator, error)
- func (o *Object) Len() (int64, error)
- func (o *Object) Ptr() uintptr
- func (o *Object) Repr() (string, error)
- func (o *Object) SetAttr(name string, value any) error
- func (o *Object) SetItem(key, value any) error
- func (o *Object) String() string
- type Option
- type PyObjectdeprecated
- type PyObjecter
- type PythonError
- type Runtime
- func (r *Runtime) Close() error
- func (r *Runtime) Eval(expr string) (*Object, error)
- func (r *Runtime) Exec(code string) error
- func (r *Runtime) FromBool(v bool) *Object
- func (r *Runtime) FromFloat64(v float64) *Object
- func (r *Runtime) FromGoValue(v any) (*Object, error)
- func (r *Runtime) FromInt64(v int64) *Object
- func (r *Runtime) FromString(v string) *Object
- func (r *Runtime) GILEnsure() *GILState
- func (r *Runtime) Import(name string) (*Object, error)
- func (r *Runtime) ImportModule(name string) (PyObject, error)deprecated
- func (r *Runtime) NewDict(kvs ...any) (*Object, error)
- func (r *Runtime) NewList(items ...any) (*Object, error)
- func (r *Runtime) NewSet(items ...any) (*Object, error)
- func (r *Runtime) NewTuple(items ...any) (*Object, error)
- func (r *Runtime) None() *Object
- func (r *Runtime) PythonVersion() string
- func (r *Runtime) PythonVersionInfo() (major, minor, micro int)
- func (r *Runtime) RegisterFunc(name string, fn any) error
- func (r *Runtime) ReleaseGIL() *ThreadStatedeprecated
- func (r *Runtime) RunAsync(expr string) (*Object, error)
- func (r *Runtime) RunAsyncGo(expr string) <-chan AsyncResult
- func (r *Runtime) StartEventLoop() (*EventLoop, error)
- func (r *Runtime) With(obj *Object, fn func(value *Object) error) error
- func (r *Runtime) WithGIL(fn func() error) error
- type ThreadState
- func (ts *ThreadState) Restore()deprecated
Examples ¶
Constants ¶
const ( PyLT = 0 // < PyLE = 1 // <= PyEQ = 2 // == PyNE = 3 // != PyGT = 4 // > PyGE = 5 // >= )
Python comparison operator constants for use with Compare.
Variables ¶
var ErrAlreadyClosed = errors.New("pyffi: runtime already closed")
ErrAlreadyClosed is returned when operations are attempted on a closed Runtime.
var ErrNilObject = errors.New("pyffi: operation on nil Object")
ErrNilObject is returned when an operation is attempted on a nil Object.
var ErrPythonNotFound = errors.New("pyffi: shared library not found")
ErrPythonNotFound is returned when no suitable Python shared library can be located on the system.
Functions ¶
func ClearCache ¶
func ClearCache() error
ClearCache removes all cached venvs created by Dependencies().
func UVAvailable ¶
func UVAvailable() bool
UVAvailable reports whether the uv command is available on PATH.
Types ¶
type AsyncResult ¶
AsyncResult holds the result of an asynchronous Python operation.
type BoundModule ¶
type BoundModule struct {
// contains filtered or unexported fields
}
BoundModule wraps an imported Python module for convenient function calls.
func Bind ¶
func Bind(rt *Runtime, moduleName string) (*BoundModule, error)
Bind imports a Python module and wraps it for dynamic function calls.
func (*BoundModule) Attr ¶
func (m *BoundModule) Attr(name string) *Object
Attr returns a named attribute from the module.
func (*BoundModule) Call ¶
func (m *BoundModule) Call(name string, args ...any) (*Object, error)
Call calls a named function on the module with positional arguments. If the last argument is of type KW, it is used as keyword arguments.
func (*BoundModule) Close ¶
func (m *BoundModule) Close() error
Close releases the underlying module reference.
func (*BoundModule) Has ¶
func (m *BoundModule) Has(name string) bool
Has reports whether the module has an attribute with the given name.
func (*BoundModule) Names ¶
func (m *BoundModule) Names() ([]string, error)
Names returns the list of public attribute names on the module.
type EventLoop ¶
type EventLoop struct {
// contains filtered or unexported fields
}
EventLoop manages a persistent asyncio event loop running in a dedicated goroutine. Coroutines can be submitted from any goroutine via Go or Submit, and results are delivered through channels.
The event loop goroutine holds the GIL while executing Python code, but releases it during I/O waits, allowing other goroutines to acquire the GIL for their own Python operations.
func (*EventLoop) Go ¶
func (el *EventLoop) Go(asyncFn *Object, args ...any) (<-chan AsyncResult, error)
Go submits a coroutine created by calling asyncFn(args...) to the event loop. Returns a channel that receives the result when the coroutine completes.
The caller must hold the GIL when calling this method (e.g., inside WithGIL). The result Object in the channel must also be accessed with the GIL held.
type GILState ¶
type GILState struct {
// contains filtered or unexported fields
}
GILState represents a held GIL. It must be released by calling Release().
type Iterator ¶
type Iterator struct {
// contains filtered or unexported fields
}
Iterator wraps a Python iterator object.
type LibraryNotFoundError ¶
type LibraryNotFoundError struct {
Searched []string
}
LibraryNotFoundError provides detailed information about which paths were searched when Python could not be found.
func (*LibraryNotFoundError) Error ¶
func (e *LibraryNotFoundError) Error() string
func (*LibraryNotFoundError) Unwrap ¶
func (e *LibraryNotFoundError) Unwrap() error
type Object ¶
type Object struct {
// contains filtered or unexported fields
}
Object wraps a CPython PyObject* with explicit lifecycle management. Call Close() to release the reference when done. A GC finalizer provides a safety net, but explicit Close() is strongly recommended.
The zero value and nil receiver are safe — all methods return ErrNilObject or nil as appropriate.
Objects are not safe for concurrent use from multiple goroutines. Use Runtime.WithGIL to access Python objects from different goroutines.
func (*Object) Attr ¶
Attr returns the named attribute. Returns nil if the attribute does not exist, which allows chaining: obj.Attr("path").Attr("join").Call(...) Use AttrErr if you need the error.
func (*Object) Call ¶
Call calls this object with the given arguments. If the last argument is of type KW, it is used as keyword arguments.
func (*Object) CallAsync ¶
CallAsync calls an async Python function with the given arguments, running the resulting coroutine with asyncio.run().
func (*Object) CallAsyncGo ¶
func (o *Object) CallAsyncGo(args ...any) <-chan AsyncResult
CallAsyncGo calls an async Python function in a background goroutine. The caller's goroutine is not blocked. The GIL must have been released via ReleaseGIL before calling this method.
The returned Object (if any) must be accessed with the GIL held.
func (*Object) Close ¶
Close releases the Python reference. It is safe to call multiple times or on a nil receiver.
func (*Object) Compare ¶
Compare performs a rich comparison between this object and other. Use PyLT, PyLE, PyEQ, PyNE, PyGT, PyGE as the op argument.
func (*Object) Contains ¶
Contains checks if item is in the object (equivalent to Python's "in" operator).
func (*Object) Exit ¶
Exit calls __exit__ on the object (context manager protocol). If err is nil, None is passed for all three arguments. If err is a *PythonError, the exception info is passed (as None values, since reconstructing the original exception is not supported).
func (*Object) Iter ¶
Iter returns an iterator over the object's elements. The caller should call Next() in a loop until it returns nil, nil.
type Option ¶
type Option func(*config)
Option configures a Runtime.
func Dependencies ¶
Dependencies declares Python package dependencies inline. A cached venv is created with the specified packages installed. Requires uv to be installed.
func WithLibraryPath ¶
WithLibraryPath sets an explicit path to the Python shared library, bypassing auto-detection.
func WithSkipFinalize ¶ added in v0.4.0
func WithSkipFinalize() Option
WithSkipFinalize skips Py_Finalize on Close. Use this when Python extensions (e.g. PyTorch) cause SIGSEGV during interpreter shutdown. Resources are reclaimed by the OS when the process exits.
func WithUV ¶
func WithUV() Option
WithUV configures the Runtime to prefer Python installations managed by uv. If uv is not found, it falls back to normal detection without error.
func WithUVProject ¶
WithUVProject configures the Runtime to use a uv-managed project. It reads pyproject.toml from projectDir, runs `uv sync`, and uses the resulting .venv's Python with all declared dependencies available.
func WithVersion ¶
WithVersion constrains auto-detection to a specific Python major.minor.
type PyObjecter ¶ added in v0.3.0
type PyObjecter interface {
Object() *Object
}
PyObjecter is implemented by types that wrap a Python object. pyffi-gen generated types satisfy this interface automatically. When passed to Python, the underlying object is used directly.
type PythonError ¶
type PythonError struct {
Context string // Go-side context, e.g. "Exec", "Import(\"foo\")"
Type string // Python exception type, e.g. "ValueError"
Message string // Exception message
Traceback string // Full traceback string (Python 3.12+)
}
PythonError represents an error that originated in the Python interpreter.
func (*PythonError) Error ¶
func (e *PythonError) Error() string
type Runtime ¶
type Runtime struct {
// contains filtered or unexported fields
}
Runtime holds a loaded CPython shared library and its bound C-API functions. Only one Runtime should be active per process because Py_Initialize/Py_Finalize operate on global state. For multi-goroutine usage, use WithGIL or GILEnsure.
func New ¶
New creates a new Python runtime. It loads the CPython shared library and calls Py_Initialize.
The caller must call Close when finished. Options can customize the Python version or library path.
func (*Runtime) Close ¶
Close calls Py_Finalize and releases the library handle. It is safe to call Close multiple times.
WARNING: After Close, creating a new Runtime in the same process may fail because CPython does not fully support Py_Finalize followed by Py_Initialize. Design your application to use a single Runtime for the process lifetime.
func (*Runtime) FromFloat64 ¶
FromFloat64 creates a Python float from a Go float64.
func (*Runtime) FromGoValue ¶
FromGoValue creates a Python object from an arbitrary Go value.
func (*Runtime) FromString ¶
FromString creates a Python str from a Go string.
func (*Runtime) GILEnsure ¶
GILEnsure acquires the GIL and locks the current goroutine to its OS thread. The caller must call Release() when done.
With auto-GIL, most users do not need to call this directly. It is still useful for batching multiple operations under a single GIL acquisition.
func (*Runtime) ImportModule
deprecated
func (*Runtime) NewDict ¶
NewDict creates a Python dict from alternating key-value pairs. Keys must be strings. Panics if an odd number of arguments is given.
func (*Runtime) PythonVersion ¶
PythonVersion returns the Python version string (e.g. "3.13.2 (main, ...)").
func (*Runtime) PythonVersionInfo ¶
PythonVersionInfo returns the major, minor, and micro version numbers.
func (*Runtime) RegisterFunc ¶
RegisterFunc registers a Go function that can be called from Python. The function is accessible via `import go_bridge; go_bridge.name(args)`.
Supported function signatures:
- func()
- func(args...) result
- func(args...) (result, error)
- func(args...) error
func (*Runtime) ReleaseGIL
deprecated
func (r *Runtime) ReleaseGIL() *ThreadState
ReleaseGIL is a no-op that returns a dummy ThreadState. It is retained for backward compatibility.
Deprecated: With auto-GIL, the init GIL is released automatically by New(). All public methods acquire the GIL as needed.
func (*Runtime) RunAsync ¶
RunAsync evaluates a Python expression that returns a coroutine and executes it with asyncio.run(), returning the result.
func (*Runtime) RunAsyncGo ¶
func (r *Runtime) RunAsyncGo(expr string) <-chan AsyncResult
RunAsyncGo runs the async expression in a background goroutine. The caller's goroutine is not blocked. The GIL must have been released via ReleaseGIL before calling this method.
The returned Object (if any) must be accessed with the GIL held.
func (*Runtime) StartEventLoop ¶
StartEventLoop creates an asyncio event loop and runs it in a background goroutine. The GIL must be held by the caller (i.e., do NOT call ReleaseGIL before this).
Call Stop to shut down the event loop when done.
func (*Runtime) With ¶
With executes fn with the context manager obj, calling __enter__ before and __exit__ after (even if fn returns an error).
type ThreadState ¶
type ThreadState struct {
// contains filtered or unexported fields
}
ThreadState is the saved thread state from releasing the init GIL.
func (*ThreadState) Restore
deprecated
func (ts *ThreadState) Restore()
Restore is a no-op. It is retained for backward compatibility.
Deprecated: With auto-GIL, the init GIL is released automatically by New(). ReleaseGIL and Restore are no longer needed.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
pyffi-gen
command
pyffi-gen generates Go type-safe bindings from Python packages.
|
pyffi-gen generates Go type-safe bindings from Python packages. |
|
internal
|
|
|
gen
Package gen implements Python module introspection and Go code generation.
|
Package gen implements Python module introspection and Go code generation. |