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.
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.
- 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.zipcontains a trimmed CPythonLib/tree and is embedded in the module. A zero-valueConfigautomatically extracts it for local execution, whileNewStdlibMemFSserves 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 fromgoccy/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 verifychecks the signed release provenance for bothpython.goandstdlib.zip, and CI runs the same verification before tests. - Isolated multi-interpreter API. Each
Interpreterowns 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-memoryMemFS; - per-path read/write decisions with
FSAccess; - guest environment variables with
Env(hostos.Environis not leaked); - guest
stdin,stdout, andstderrwith explicitio.Reader/io.Writervalues; - 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.
- filesystem scope with
- Python command front-end.
cmd/pythonand packagecliprovide a Python-like command entry point forpython -c <command>andpython <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.
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.
go get github.com/goccy/go-pythongo-python requires Go 1.25 or newer.
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)
}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) // 5050interrupt, 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 KeyboardInterruptgo run ./cmd/python -c 'import sys; print(sys.version)'
go run ./cmd/python ./script.py arg1 arg2The 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 verifyThe target:
- computes the SHA-256 digest of
python.goandstdlib.zip; - fetches the public attestation bundle for each digest from the GitHub attestations API;
- runs
gh attestation verify --bundlewith--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.
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 ./...MIT.