pyffi

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 22, 2026 License: MIT Imports: 20 Imported by: 0

README

pyffi

Go bindings for CPython via puregono Cgo required.

rt, _ := pyffi.New()
defer rt.Close()

rt.Exec(`x = 1 + 2`)
result, _ := rt.Eval("x * 10")
defer result.Close()
fmt.Println(result.Int64()) // 30

Features

  • No Cgo — pure Go, uses purego for FFI. CGO_ENABLED=0 builds work
  • Auto-GIL — all methods are goroutine-safe out of the box
  • Full type conversion — bool, int, float, string, bytes, list, tuple, dict, set
  • Async supportRunAsync, CallAsync, EventLoop for asyncio integration
  • Callbacks — register Go functions callable from Python (with kwargs)
  • Free-threaded Python — automatic detection of Python 3.13t+ builds
  • uv integration — auto-detect, auto-install dependencies, project venv support
  • Code generationpyffi-gen generates type-safe Go bindings from Python modules
  • Platform support — macOS, Linux, and Windows

Install

go get github.com/i2y/pyffi

Python 3.12+ is needed at runtime. pyffi auto-detects system Python, Homebrew, and uv-managed installations. If Python is not installed:

# Recommended: install uv, then let pyffi auto-detect
curl -LsSf https://astral.sh/uv/install.sh | sh
uv python install 3.14

Quick Start

Execute and Evaluate
rt, _ := pyffi.New()
defer rt.Close()

// Execute statements
rt.Exec(`
def greet(name):
    return f"Hello, {name}!"
`)

// Evaluate expressions
result, _ := rt.Eval(`greet("World")`)
defer result.Close()
s, _ := result.GoString() // "Hello, World!"
Resource Management (Close)

pyffi wraps Python objects as *Object values that hold a reference to the underlying CPython object. These must be released with Close() to avoid memory leaks:

Type Close required? Why
*Runtime Yes (defer rt.Close()) Calls Py_Finalize to shut down the interpreter
*Object Yes (defer obj.Close()) Calls Py_DecRef to release the Python reference

Every method that returns *ObjectEval, Import, Attr, Call, GetItem, Iter().Next(), etc. — requires the caller to close the result. A GC finalizer exists as a safety net, but explicit Close() is strongly recommended.

Primitive extraction methods (Int64, Float64, GoString, Bool, GoSlice, GoMap, GoValue) return plain Go values that do not need closing.

Import Modules
math, _ := rt.Import("math")
defer math.Close()

pi := math.Attr("pi")
defer pi.Close()
f, _ := pi.Float64() // 3.141592653589793
Collections
// Create
list, _ := rt.NewList(1, "two", 3.0)
dict, _ := rt.NewDict("name", "Go", "year", 2009)
tuple, _ := rt.NewTuple("a", "b", "c")
set, _ := rt.NewSet(1, 2, 3)

// Access
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)
r, _ := list.Repr()             // repr(list)

// Iterate
iter, _ := list.Iter()
defer iter.Close()
for {
    item, _ := iter.Next()
    if item == nil { break }
    defer item.Close()
    // ...
}

// Compare
eq, _ := a.Equals(b)            // a == b
lt, _ := a.Compare(b, pyffi.PyLT)  // a < b
Type Conversions
Go → Python Python → Go
boolbool boolbool
int, int8int64int intint64
uint, uint8uint64int floatfloat64
float32, float64float strstring
stringstr bytes, bytearray[]byte
[]bytebytes list, tuple, set[]any
[]anylist dictmap[string]any
map[string]anydict Nonenil
nilNone

Other Python types (functions, classes, instances, modules, etc.) are returned as *Object and can be manipulated via Attr(), Call(), GetItem(), etc:

// Classes: Call() instantiates
cls := mod.Attr("MyClass")
defer cls.Close()
instance, _ := cls.Call("arg1", 42)  // MyClass("arg1", 42)
defer instance.Close()

// Instance methods and attributes
name, _ := instance.Attr("name").GoString()
result, _ := instance.Attr("method").Call()
defer result.Close()

// Functions: first-class objects
fn := mod.Attr("some_function")
defer fn.Close()
result, _ := fn.Call(args...)
Goroutine Safety

All methods automatically acquire the GIL. No manual management needed:

var wg sync.WaitGroup
for i := range 10 {
    wg.Add(1)
    go func(n int) {
        defer wg.Done()
        obj := rt.FromInt64(int64(n))
        defer obj.Close()
        // Safe from any goroutine
    }(i)
}
wg.Wait()

For batching (reduces GIL overhead):

rt.WithGIL(func() error {
    rt.Exec("a = 1")
    rt.Exec("b = 2")
    rt.Exec("c = a + b")
    return nil
})
Async Python
// Synchronous
result, _ := rt.RunAsync("fetch_data('https://example.com')")

// Non-blocking (background goroutine)
ch := rt.RunAsyncGo("fetch_data('https://example.com')")
ar := <-ch // pyffi.AsyncResult{Value, Err}

// Call async functions directly
fn := mod.Attr("async_func")
result, _ := fn.CallAsync(arg1, arg2)
Callbacks
rt.RegisterFunc("add", func(a, b int) int {
    return a + b
})

// With keyword arguments
rt.RegisterFunc("greet", func(name string, kw map[string]any) string {
    greeting := "hello"
    if g, ok := kw["greeting"]; ok {
        greeting = g.(string)
    }
    return greeting + " " + name
})

rt.Exec(`
import go_bridge
print(go_bridge.add(1, 2))               # 3
print(go_bridge.greet("Go", greeting="hi"))  # hi Go
`)
Context Managers
rt.With(resource, func(value *pyffi.Object) error {
    // __enter__ called, value is the result
    // __exit__ called automatically (even on error)
    return nil
})

Python & Dependency Management with uv

pyffi integrates with uv for Python discovery and dependency management. uv is a fast Python package manager written in Rust.

Auto-Detect uv-Managed Python
// Prefer uv-managed Python installations
rt, _ := pyffi.New(pyffi.WithUV())
Inline Dependencies

Automatically creates a hash-based cached venv in ~/.cache/pyffi/venvs/ and installs packages:

rt, _ := pyffi.New(pyffi.Dependencies("numpy", "pandas", "requests"))
defer rt.Close()

rt.Exec(`
import numpy as np
import pandas as pd
print(np.array([1, 2, 3]))
`)

The venv is cached by a hash of the dependency list — subsequent runs skip installation.

Project venv (pyproject.toml)

For projects with a pyproject.toml, use WithUVProject to run uv sync and use the project's venv:

rt, _ := pyffi.New(pyffi.WithUVProject("/path/to/python-project"))
defer rt.Close()

// All project dependencies are available
rt.Exec(`from mypackage import something`)
Explicit Library Path

Skip auto-detection entirely by pointing directly to the Python shared library:

rt, _ := pyffi.New(pyffi.WithLibraryPath("/usr/lib/libpython3.14.so"))

This is useful in containers where the Python location is known at build time.

Code Generation

pyffi-gen generates type-safe Go bindings from Python modules by introspecting them at build time:

# Install
go install github.com/i2y/pyffi/cmd/pyffi-gen@latest

# Generate bindings for a Python module
pyffi-gen --module numpy --out ./gen/numpypkg --dependencies numpy

# Preview without writing files
pyffi-gen --module json --dry-run

# Use a config file
pyffi-gen --config pyffi-gen.yaml

# Initialize a new project with config scaffolding
pyffi-gen init --module numpy,pandas

The generated code provides a low-level typed Module wrapper. For production use, create a higher-level Go package that wraps the generated code with idiomatic APIs:

yourpkg/
├── internal/sdk/       # Generated by pyffi-gen (DO NOT EDIT)
│   └── sdk.go
├── yourpkg.go          # Your idiomatic Go API wrapping internal/sdk
├── options.go          # Option types
└── ...

You don't need to wrap every generated function — pick and choose what to expose. You can also bypass the generated bindings entirely and use rt.Exec() / rt.Eval() with Python code strings for cases where that's simpler (e.g., variadic expression arguments). Mixing both approaches in the same package is fine. The polarsgo package in this repository is a practical example — it uses the generated internal/sdk/ for methods with fixed signatures (Head, Tail, Join, Sort, etc.) while using inline Python code for expression-based methods (Filter, WithColumns, GroupBy).

Docker Deployment

pyffi works well in Docker with a multi-stage build. Since pyffi uses purego (no Cgo), the Go binary can be built with CGO_ENABLED=0:

# Stage 1: Build Go binary (no Python needed)
FROM golang:1.26 AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/myapp ./cmd/myapp

# Stage 2: Runtime with Python
FROM python:3.14-slim
RUN pip install uv
COPY --from=builder /app/myapp /usr/local/bin/myapp

# Option A: Use pyproject.toml
COPY pyproject.toml uv.lock ./
RUN uv sync
# In Go: pyffi.New(pyffi.WithUVProject("."))

# Option B: Or let pyffi.Dependencies() install at first run
# In Go: pyffi.New(pyffi.Dependencies("numpy", "pandas"))

CMD ["myapp"]
Optimized: No uv at Runtime

For smaller images, install dependencies at build time and use WithLibraryPath to skip uv at runtime:

FROM python:3.14-slim AS python-deps
RUN pip install uv
COPY pyproject.toml uv.lock ./
RUN uv sync

FROM python:3.14-slim
COPY --from=python-deps /.venv /.venv
COPY --from=builder /app/myapp /usr/local/bin/myapp
# No uv needed at runtime
# In Go: pyffi.New(pyffi.WithLibraryPath("/usr/local/lib/libpython3.14.so"))
# Then add /.venv/lib/python3.14/site-packages to sys.path via Exec
CMD ["myapp"]

Wrapper Packages

pyffi-powered Go bindings for popular Python libraries. Each is an independent Go module — install only what you need.

polarsgo — DataFrames

Fast DataFrame operations from Go using Polars. Filter, sort, join, group, aggregate, LazyFrame optimization, and SQL queries.

go get github.com/i2y/pyffi/polarsgo
pl, _ := polarsgo.New()
defer pl.Close()

df, _ := pl.ReadCSV("data.csv")
result, _ := df.Filter("col('age') > 30").Sort("age", true)
fmt.Println(result)

// SQL queries
sqlResult, _ := pl.SQL("SELECT dept, AVG(age) FROM t GROUP BY dept", map[string]*polarsgo.DataFrame{"t": df})

See the polarsgo README for the full API including LazyFrame, Join, GroupBy, and more.

sbert — Sentence Embeddings

Generate semantic embeddings using 15,000+ sentence-transformers models.

go get github.com/i2y/pyffi/sbert
model, _ := sbert.New("all-MiniLM-L6-v2")
defer model.Close()

embeddings, _ := model.Encode([]string{"Hello world", "Go is great"})
sim, _ := model.Similarity(embeddings, embeddings)

See the sbert README.

hfpipe — Hugging Face Pipelines

Local ML inference — text generation, classification, summarization, and more.

go get github.com/i2y/pyffi/hfpipe
pipe, _ := hfpipe.New("text-classification", "distilbert/distilbert-base-uncased-finetuned-sst-2-english")
defer pipe.Close()

results, _ := pipe.Run("I love this movie!")  // [{label:POSITIVE score:0.9999}]

See the hfpipe README.

dspygo — DSPy

Program (not prompt) language models with typed signatures, pipelines, and automatic prompt optimization.

go get github.com/i2y/pyffi/dspygo
client, _ := dspygo.New(dspygo.WithLM("openai/gpt-4o-mini"))
defer client.Close()

classify := client.PredictSig(dspygo.Signature{
    Doc: "Classify sentiment.",
    Inputs:  []dspygo.Field{{Name: "sentence", Type: "str"}},
    Outputs: []dspygo.Field{{Name: "sentiment", Type: `Literal["positive", "negative", "neutral"]`}},
})
result, _ := classify.Call(dspygo.KV{"sentence": "I love it!"})

See the dspygo README.

outlines — Structured Generation

Constrained decoding — guarantee LLM output matches a JSON schema, regex, or choice set.

go get github.com/i2y/pyffi/outlines
model, _ := outlines.NewOllama("llama3.2")
defer model.Close()

result, _ := model.PydanticJSON("Generate a user profile.", "Profile", map[string]string{"name": "str", "age": "int"})

See the outlines README.

diffusersgo — Image Generation

Generate images from text using Hugging Face Diffusers (Stable Diffusion, FLUX, etc.).

go get github.com/i2y/pyffi/diffusersgo
pipe, _ := diffusersgo.New("stable-diffusion-v1-5/stable-diffusion-v1-5",
    diffusersgo.WithDevice("mps"),
    diffusersgo.WithDtype("float16"),
)
defer pipe.Close()

img, _ := pipe.TextToImage("A cat in space, oil painting")
img.Save("cat.png")

See the diffusersgo README.

smolagentsgo — Lightweight Agents

Build agents with smolagents that write Python code to orchestrate tools and solve multi-step tasks.

go get github.com/i2y/pyffi/smolagentsgo
client, _ := smolagentsgo.New(
    smolagentsgo.WithLiteLLM("anthropic/claude-3-haiku-20240307", apiKey),
)
defer client.Close()

result, _ := client.Run("What is 15 * 23?")
fmt.Println(result) // 345

See the smolagentsgo README.

casdk — Claude Agent SDK

Go wrapper for the Claude Agent SDK with hooks, plugins, and in-process MCP tools.

go get github.com/i2y/pyffi/casdk
client, _ := casdk.New()
defer client.Close()

for msg, err := range client.Query(ctx, "What is 2+2?", casdk.WithMaxTurns(1)) {
    if err != nil { log.Fatal(err) }
    fmt.Println(msg.Text())
}

See the casdk README.

datasetsgo — Hugging Face Datasets

Access 200,000+ datasets on the Hugging Face Hub from Go.

go get github.com/i2y/pyffi/datasetsgo
client, _ := datasetsgo.New()
defer client.Close()

ds, _ := client.Load("rotten_tomatoes")
train := ds.Split("train")
fmt.Println(train.Len())       // 8530
row, _ := train.Row(0)
fmt.Println(row["text"])

See the datasetsgo README.

peftgo — LoRA Fine-Tuning

Fine-tune large models with PEFT (LoRA, etc.) from Go.

go get github.com/i2y/pyffi/peftgo
model, _ := peftgo.NewModel("meta-llama/Llama-3-8B", peftgo.WithDevice("auto"))
defer model.Close()

model.ApplyLoRA(peftgo.LoRAConfig{Rank: 16, Alpha: 32})
trainable, total, pct := model.TrainableParams()
fmt.Printf("Trainable: %d / %d (%.2f%%)\n", trainable, total, pct)
model.SaveAdapter("./my-adapter")

See the peftgo README.

Known Limitations

  • Process exit SIGSEGV: When using Python libraries with background threads (PyTorch, datasets, etc.), a SIGSEGV may occur during process shutdown. This does not affect program correctness — it only happens after main() returns and the OS is about to reclaim all resources anyway. Long-running servers are unaffected since the issue only occurs at process exit.
  • Py_Finalize instability: CPython's Py_Finalize does not work reliably with all extension modules. Use pyffi.WithSkipFinalize() if you encounter issues. Resources are reclaimed by the OS on process exit.

For LLM Agents

This project includes AgentSkills for AI coding assistants:

Build & Test

make ci          # fmt + vet + build + test
make test        # verbose tests
make bench       # benchmarks
make lint        # staticcheck (if installed)

License

MIT

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

Examples

Constants

View Source
const (
	PyLT = 0 // <
	PyLE = 1 // <=
	PyEQ = 2 // ==
	PyNE = 3 // !=
	PyGT = 4 // >
	PyGE = 5 // >=
)

Python comparison operator constants for use with Compare.

Variables

View Source
var ErrAlreadyClosed = errors.New("pyffi: runtime already closed")

ErrAlreadyClosed is returned when operations are attempted on a closed Runtime.

View Source
var ErrNilObject = errors.New("pyffi: operation on nil Object")

ErrNilObject is returned when an operation is attempted on a nil Object.

View Source
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.

func UVVersion

func UVVersion() (string, error)

UVVersion returns the uv version string (e.g., "0.9.20").

Types

type AsyncResult

type AsyncResult struct {
	Value *Object
	Err   error
}

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) CallKw

func (m *BoundModule) CallKw(name string, args []any, kwargs map[string]any) (*Object, error)

CallKw calls a named function with positional and 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.

func (*EventLoop) Stop

func (el *EventLoop) Stop() error

Stop signals the event loop to stop and waits for it to finish.

func (*EventLoop) Submit

func (el *EventLoop) Submit(coro *Object) (<-chan AsyncResult, error)

Submit submits an existing coroutine object to the event loop. The coroutine will be closed after submission.

The caller must hold the GIL when calling this method.

type GILState

type GILState struct {
	// contains filtered or unexported fields
}

GILState represents a held GIL. It must be released by calling Release().

func (*GILState) Release

func (g *GILState) Release()

Release releases the GIL and unlocks the OS thread.

type Iterator

type Iterator struct {
	// contains filtered or unexported fields
}

Iterator wraps a Python iterator object.

func (*Iterator) Close

func (it *Iterator) Close()

Close releases the iterator.

func (*Iterator) Next

func (it *Iterator) Next() (*Object, error)

Next returns the next item from the iterator. Returns (nil, nil) when the iterator is exhausted.

type KW

type KW = map[string]any

KW is a convenience type for keyword arguments.

obj.Call(data, pyffi.KW{"indent": 4})

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

func (o *Object) Attr(name string) *Object

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) AttrErr

func (o *Object) AttrErr(name string) (*Object, error)

AttrErr returns the named attribute or an error.

func (*Object) Bool

func (o *Object) Bool() (bool, error)

Bool returns the Python bool value as a Go bool.

func (*Object) Call

func (o *Object) Call(args ...any) (*Object, error)

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

func (o *Object) CallAsync(args ...any) (*Object, error)

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) CallKw

func (o *Object) CallKw(args []any, kwargs map[string]any) (*Object, error)

CallKw calls this object with positional and keyword arguments.

func (*Object) Close

func (o *Object) Close() error

Close releases the Python reference. It is safe to call multiple times or on a nil receiver.

func (*Object) Compare

func (o *Object) Compare(other *Object, op int) (bool, error)

Compare performs a rich comparison between this object and other. Use PyLT, PyLE, PyEQ, PyNE, PyGT, PyGE as the op argument.

func (*Object) Contains

func (o *Object) Contains(item any) (bool, error)

Contains checks if item is in the object (equivalent to Python's "in" operator).

func (*Object) DelItem

func (o *Object) DelItem(key any) error

DelItem deletes obj[key] using the Python __delitem__ protocol.

func (*Object) Enter

func (o *Object) Enter() (*Object, error)

Enter calls __enter__ on the object (context manager protocol).

func (*Object) Equals

func (o *Object) Equals(other *Object) (bool, error)

Equals returns true if this object equals other (Python ==).

func (*Object) Exit

func (o *Object) Exit(err error) error

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) Float64

func (o *Object) Float64() (float64, error)

Float64 returns the Python float value as a Go float64.

func (*Object) GetItem

func (o *Object) GetItem(key any) (*Object, error)

GetItem returns obj[key] using the Python __getitem__ protocol.

func (*Object) GoBytes

func (o *Object) GoBytes() ([]byte, error)

GoBytes converts a Python bytes or bytearray to a Go []byte.

func (*Object) GoMap

func (o *Object) GoMap() (map[string]any, error)

GoMap converts a Python dict to a Go map[string]any.

func (*Object) GoSlice

func (o *Object) GoSlice() ([]any, error)

GoSlice converts a Python list to a Go []any.

func (*Object) GoString

func (o *Object) GoString() (string, error)

GoString returns the Python str value as a Go string.

func (*Object) GoValue

func (o *Object) GoValue() (any, error)

GoValue converts the Python object to the most appropriate Go type.

func (*Object) Int64

func (o *Object) Int64() (int64, error)

Int64 returns the Python int value as a Go int64.

func (*Object) IsNone

func (o *Object) IsNone() bool

IsNone reports whether this object is Python's None.

func (*Object) Iter

func (o *Object) Iter() (*Iterator, error)

Iter returns an iterator over the object's elements. The caller should call Next() in a loop until it returns nil, nil.

func (*Object) Len

func (o *Object) Len() (int64, error)

Len returns the length of the object (equivalent to Python's len()).

func (*Object) Ptr

func (o *Object) Ptr() uintptr

Ptr returns the raw PyObject* pointer.

func (*Object) Repr

func (o *Object) Repr() (string, error)

Repr returns the Python repr() of the object.

func (*Object) SetAttr

func (o *Object) SetAttr(name string, value any) error

SetAttr sets the named attribute to the given Go value.

func (*Object) SetItem

func (o *Object) SetItem(key, value any) error

SetItem sets obj[key] = value using the Python __setitem__ protocol.

func (*Object) String

func (o *Object) String() string

String returns the Python str() representation.

type Option

type Option func(*config)

Option configures a Runtime.

func Dependencies

func Dependencies(deps ...string) Option

Dependencies declares Python package dependencies inline. A cached venv is created with the specified packages installed. Requires uv to be installed.

func WithLibraryPath

func WithLibraryPath(path string) Option

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

func WithUVProject(projectDir string) Option

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

func WithVersion(major, minor int) Option

WithVersion constrains auto-detection to a specific Python major.minor.

type PyObject deprecated

type PyObject uintptr

PyObject represents a CPython PyObject* as an opaque handle. The zero value represents NULL.

Deprecated: Use *Object instead for new code.

func (PyObject) IsNull

func (o PyObject) IsNull() bool

IsNull reports whether the PyObject pointer is NULL.

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

func New(opts ...Option) (*Runtime, error)

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

func (r *Runtime) Close() error

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) Eval

func (r *Runtime) Eval(expr string) (*Object, error)

Eval evaluates a Python expression and returns the result.

func (*Runtime) Exec

func (r *Runtime) Exec(code string) error

Exec executes the given Python source code in the __main__ module.

func (*Runtime) FromBool

func (r *Runtime) FromBool(v bool) *Object

FromBool creates a Python bool from a Go bool.

func (*Runtime) FromFloat64

func (r *Runtime) FromFloat64(v float64) *Object

FromFloat64 creates a Python float from a Go float64.

func (*Runtime) FromGoValue

func (r *Runtime) FromGoValue(v any) (*Object, error)

FromGoValue creates a Python object from an arbitrary Go value.

func (*Runtime) FromInt64

func (r *Runtime) FromInt64(v int64) *Object

FromInt64 creates a Python int from a Go int64.

func (*Runtime) FromString

func (r *Runtime) FromString(v string) *Object

FromString creates a Python str from a Go string.

func (*Runtime) GILEnsure

func (r *Runtime) GILEnsure() *GILState

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) Import

func (r *Runtime) Import(name string) (*Object, error)

Import imports a Python module by name and returns it as an *Object.

func (*Runtime) ImportModule deprecated

func (r *Runtime) ImportModule(name string) (PyObject, error)

ImportModule imports a Python module by name and returns it as a PyObject. The caller receives a new reference.

Deprecated: Use Import instead for new code.

func (*Runtime) NewDict

func (r *Runtime) NewDict(kvs ...any) (*Object, error)

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) NewList

func (r *Runtime) NewList(items ...any) (*Object, error)

NewList creates a Python list from the given Go values.

func (*Runtime) NewSet

func (r *Runtime) NewSet(items ...any) (*Object, error)

NewSet creates a Python set from the given Go values.

func (*Runtime) NewTuple

func (r *Runtime) NewTuple(items ...any) (*Object, error)

NewTuple creates a Python tuple from the given Go values.

func (*Runtime) None

func (r *Runtime) None() *Object

None returns a new reference to Python's None object.

func (*Runtime) PythonVersion

func (r *Runtime) PythonVersion() string

PythonVersion returns the Python version string (e.g. "3.13.2 (main, ...)").

func (*Runtime) PythonVersionInfo

func (r *Runtime) PythonVersionInfo() (major, minor, micro int)

PythonVersionInfo returns the major, minor, and micro version numbers.

func (*Runtime) RegisterFunc

func (r *Runtime) RegisterFunc(name string, fn any) error

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

func (r *Runtime) RunAsync(expr string) (*Object, error)

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

func (r *Runtime) StartEventLoop() (*EventLoop, error)

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

func (r *Runtime) With(obj *Object, fn func(value *Object) error) error

With executes fn with the context manager obj, calling __enter__ before and __exit__ after (even if fn returns an error).

func (*Runtime) WithGIL

func (r *Runtime) WithGIL(fn func() error) error

WithGIL acquires the GIL, calls fn, then releases the GIL. The current goroutine is locked to its OS thread for the duration.

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.

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.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL