Typed Python 3 → idiomatic, stdlib-only Go.
Source-to-source. No runtime. The output reads like Go you would write by hand.
Quick start · Example · Subset · Architecture · FAQ
def add(a: int, b: int) -> int:
return a + b
print(add(2, 3))func add(a int64, b int64) int64 {
return (a + b)
}
func main() {
pyPrintln(add(2, 3))
}Concrete types. Real Go signatures. fmt / strconv / strings only.
Drop it into any Go module.
Other Python-to-Go tools pick one of two paths: a boxed-value runtime that runs everything but reads like assembly, or a narrow compiler that falls back to a runtime for the rest. gopygo takes a third path. Annotate your Python with PEP 484, and gopygo emits Go you would be comfortable merging into a real codebase.
The rule is strict: every value has a concrete type. When
inference stalls, gopygo stops at the Python line and tells you
which annotation to add. Programs already written with mypy
in the loop tend to fit as-is.
Requires Go 1.26 and Python 3.14 on PATH.
git clone https://github.com/tamnd/gopygo
cd gopygo
go build -o gopygo ./cmd/gopygo
./gopygo transpile hello.py -o hello.go # emit Go
./gopygo run hello.py # transpile and go run
./gopygo versionRun the snapshot suite:
./tests/run.shEvery fixture transpiles, executes under CPython and under the
emitted Go, and byte-diffs the two outputs. A grep guard fails the
suite if any emitted Go imports github.com/tamnd/gopygo —
that is how the stdlib-only promise stays honest.
hello.py:
def greet(name: str) -> str:
return "hello, " + name
for who in ["alice", "bob"]:
print(greet(who))gopygo transpile hello.py -o hello.go:
package main
import (
"fmt"
"strconv"
"strings"
)
func greet(name string) string {
return "hello, " + name
}
func main() {
for _, who := range []string{"alice", "bob"} {
pyPrintln(greet(who))
}
}
// pyRepr, pyPrintln helpers emitted once per program, only when print is used.More fixtures live under tests/fixtures/ —
each one ships the .py, the expected .go, and the expected stdout.
| Area | Supported |
|---|---|
| Types in annotations | int, float, bool, str, None, Any, list[T], dict[K,V], tuple[...] |
| Functions | Top-level def with PEP 484 parameters and return type |
| Expressions | Arithmetic, comparisons, bool ops, subscript, f-strings, ternary, list/dict literals |
| Statements | if/elif/else, while, for over range/list/str/dict, return/break/continue/pass, augmented assignment |
| Builtins | print, len, abs, min, max, int, float, str, bool, range |
| Inference | First-assignment declares; numeric widening; reassignments typecheck |
Classes / exceptions / generators / imports / async |
Out of scope — rejected at transpile time with a line number |
The architecture doc has the full emission reference and diagnostics catalogue.
cmd/gopygo/ CLI: transpile, run, version
pyast/ CPython parser bridge and JSON AST wrapper
types/ Type lattice and Python-to-Go mapping
gen/ Fused inference and emission
tests/ Fixtures (.py/.go/.expected.txt) and snapshot runner
A few thousand lines total. Readable in an afternoon.
Can I use any to paper over an unknown type? Only by
annotating with typing.Any. Inference stays concrete, by design.
Why int64 instead of int? Python ints are unbounded; Go's
int is platform-dependent. int64 sets an explicit upper bound
that stays stable across 32- and 64-bit builds.
Why route print through a helper? Python formats True and
3.0 differently from Go's fmt.Println. The pyPrintln helper
keeps stdout byte-identical to CPython, which is what makes the
snapshot tests work.
Why CPython for parsing? Reusing the reference parser keeps gopygo honest as Python evolves and frees the project to focus on typing and emission.
Does it work on Windows? Untested. pyast shells python3.14;
if that is on PATH it should work, but fixture snapshots use LF
line endings.
See ARCHITECTURE.md § FAQ for the full list.
- ARCHITECTURE.md — pipeline stages, type lattice, inference rules, design trade-offs, emission reference.
tests/fixtures/— every feature exercised as a runnable triplet.- Releases v0.1, v0.2, v0.3 — the path from bytecode dispatch to typed source translation.
MIT. See LICENSE.