Skip to content

goccy/go-python

Repository files navigation

go-python

CI Go Reference

Pure-Go CPython for Go applications.

go-python runs CPython compiled to WebAssembly and transpiled to Go by goccy/pythonwasm2go. It gives Go programs a real CPython runtime with an embedded standard library, without cgo, without a native Python installation, and without shipping a wasm runtime at execution time.

The package is built for embedding Python safely. A host application can create isolated interpreters, decide exactly what each interpreter can see or touch, and run agent-generated Python with explicit controls over filesystem, environment, stdio, networking, subprocesses, memory, and cancellation.

Why this library

gpython is a pure-Go Python VM, but it is a partial reimplementation / port of Python 3.4 and does not include many CPython C-backed modules. That is useful for some embedding use cases, but it is not the same thing as running CPython.

go-python takes a different path: CPython is built to WebAssembly, then the wasm is transpiled into ordinary Go source. The result keeps the deployment advantages of a Go dependency while running the CPython runtime and a bundled CPython standard library.

This matters most when Python code is produced or selected by an AI agent. The host process can execute familiar Python while still enforcing a Go-side policy before the guest reaches host resources.

Features

  • Pure Go, no cgo, no wasm runtime. CPython is compiled to WebAssembly and then transpiled to Go by goccy/pythonwasm2go. Applications import a Go module and build with the Go toolchain.
  • Real CPython with an embedded standard library. stdlib.zip contains a trimmed CPython Lib/ tree and is embedded in the module. A zero-value Config automatically extracts it for local execution, while NewStdlibMemFS serves it from an in-memory filesystem for fully isolated embeddings.
  • Auto-generated end-to-end. The checked-in bridge (python.go) and embedded standard library (stdlib.zip) are pulled from goccy/python-wasm. Updating the upstream release refreshes the generated CPython binding without hand-writing the runtime surface.
  • End-to-end provenance. Upstream-sourced artifacts are protected by GitHub artifact attestations. make verify checks the signed release provenance for both python.go and stdlib.zip, and CI runs the same verification before tests.
  • Isolated multi-interpreter API. Each Interpreter owns its own wasm module, linear memory, WASI host, and CPython runtime. Interpreters can run concurrently with independent globals and filesystem state.
  • Agent-oriented sandbox controls. The host can control:
    • filesystem scope with PreopenDir;
    • private filesystem backends with FS, including in-memory MemFS;
    • per-path read/write decisions with FSAccess;
    • guest environment variables with Env (host os.Environ is not leaked);
    • guest stdin, stdout, and stderr with explicit io.Reader / io.Writer values;
    • hostname resolution with Resolve;
    • outbound connect destinations with Dial;
    • socket accept/recv/send operations with NetAccess;
    • host subprocess execution with Exec;
    • wasm linear-memory growth with MaxMemoryBytes;
    • long-running Python code with Interrupt / PrepareInterrupt.
  • Python command front-end. cmd/python and package cli provide a Python-like command entry point for python -c <command> and python <file.py> [args...].
  • WASI-friendly build. Because this is pure Go, embedding applications can also cross-compile to GOOS=wasip1 GOARCH=wasm. See docs/wasip1.md.

Status

The checked-in upstream artifacts currently track goccy/python-wasm v0.1.2. The WASI verification path in this repository exercises CPython 3.14.6.

The main API supports expression / statement evaluation, persistent globals per interpreter, standard-library imports, stdout/stderr capture, isolated interpreter instances, in-memory filesystems, filesystem/network/subprocess policy hooks, memory caps, and host-triggered KeyboardInterrupt.

The command front-end currently supports -c and script-file execution. Interactive REPL mode and python -m are not implemented yet.

Installation

go get github.com/goccy/go-python

go-python requires Go 1.25 or newer.

Synopsis

Evaluate Python

package main

import (
	"fmt"

	python "github.com/goccy/go-python"
)

func main() {
	interp, err := python.NewInterpreter(python.Config{})
	if err != nil {
		panic(err)
	}
	defer interp.Close()

	res, err := interp.Eval(`
import json, math
print(json.dumps({"sqrt2": round(math.sqrt(2), 6)}))
`)
	if err != nil {
		panic(err)
	}
	if !res.Ok {
		panic(res.Error)
	}
	fmt.Print(res.Stdout)
}

Run agent-generated code in a tighter sandbox

The zero-value Config is convenient for local execution, but it is not a deny-all sandbox: the host filesystem is visible unless scoped, and networking or subprocess execution must be denied with hooks when running untrusted code. For agent-generated code, pass an explicit policy.

fsys, err := python.NewStdlibMemFS()
if err != nil {
	panic(err)
}

interp, err := python.NewInterpreter(python.Config{
	FS:             fsys, // private in-memory filesystem; StdlibDir defaults to "/"
	Env:            []string{"APP_MODE=agent"},
	MaxMemoryBytes: 64 << 20,
	NetAccess:      func(op string) bool { return false },
	Resolve:        func(host string) bool { return false },
	Dial:           func(network, ip string, port int) bool { return false },
	Exec:           func(path string, argv []string) bool { return false },
})
if err != nil {
	panic(err)
}
defer interp.Close()

res, err := interp.Eval("sum(range(101))")
if err != nil {
	panic(err)
}
if !res.Ok {
	panic(res.Error)
}
fmt.Println(res.Repr) // 5050

Interrupt a long-running evaluation

interrupt, err := interp.PrepareInterrupt()
if err != nil {
	panic(err)
}

done := make(chan python.EvalResult, 1)
go func() {
	res, _ := interp.Eval("while True:\n    pass")
	done <- res
}()

time.AfterFunc(time.Second, interrupt.Fire)
res := <-done // res.Ok == false, res.Error contains KeyboardInterrupt

Use the command front-end

go run ./cmd/python -c 'import sys; print(sys.version)'
go run ./cmd/python ./script.py arg1 arg2

Verifying provenance

The generated bridge and embedded standard library are release artifacts from goccy/python-wasm. You can verify that the local files came from the trusted upstream release workflow without a GitHub access token:

make verify

The target:

  1. computes the SHA-256 digest of python.go and stdlib.zip;
  2. fetches the public attestation bundle for each digest from the GitHub attestations API;
  3. runs gh attestation verify --bundle with --signer-workflow goccy/python-wasm/.github/workflows/release.yml.

CI runs make verify before go test ./..., so pull requests must preserve the signed upstream artifacts.

Resource footprint

The runtime cost is different from a small hand-written interpreter. This package embeds a CPython standard library archive and depends on a wasm2go-transpiled CPython engine, so cold builds and interpreter startup are heavier than a typical Go dependency. In exchange, applications get a cgo-free, cross-compilable CPython runtime whose host access is mediated through Go.

For local measurements, run:

go test ./...
cd bench
go test -bench . -benchmem ./...
go test -run TestMemoryFootprint -v ./...

License

MIT.

About

No description, website, or topics provided.

Resources

License

Stars

20 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors