Skip to main content

Parity

gopy is a behavioural twin of CPython. The line that defines behavioural parity is intentionally tight: every observable side effect should match. That includes return values, exception types, exception messages, bytecode emitted, location columns, .pyc output, and the order in which side effects happen.

How parity is enforced

Function-level translation

Every Go function under gopy has a // CPython: citation pointing to the C function it ports. Reviewers reject patches that add helper functions without a CPython counterpart, because divergence starts with one extra abstraction.

// CPython: Python/compile.c:L353 _PyAST_Compile
func Compile(mod ast.Mod, filename string, optimize int) (*Code, error) {
// ...
}

Unit tests with golden fixtures

Each subsystem has a corpus of fixtures stored as .py input with golden output: tokens, AST dumps, disassemblies, .pyc bytes, program stdout. The fixture is generated once from CPython, then gopy is required to reproduce it byte for byte.

Parity tests

parser/parity_test.go, compile/compiler_test.go, and vm/eval_test.go re-run a curated set of programs against both gopy and CPython at test time, then diff the results. Anything that differs is a parity bug.

The Python test suite

The Lib/test/ directory from CPython is vendored under stdlib/test/. The runner under v05test/ walks that tree and checks pass / fail against the upstream expected status. The sweep is the truth of record for parity at the language level.

Where the bar is

Behaviour that is documented in the language reference is load-bearing: same value, same type, same message. Behaviour that the language reference calls implementation-defined still has to match CPython on byte-equal output unless the spec explicitly says the result is unspecified.

CPython implementation details with no Python-visible behaviour (internal allocation strategy, header layout, the precise addresses returned by id) do not need to match.

What changes when there is a mismatch

Bug in gopy. Fix gopy.

The only carve-out is the surface API style. Go callers see Go idioms (interface satisfaction over duck-typing, error returns, package layout that follows the Go standard library). The Python-visible behaviour is unaffected.

Reporting a parity bug

A useful bug report has three parts:

  1. Source. Minimal Python that reproduces the mismatch.
  2. CPython output. What python3 (3.14.x) produces.
  3. gopy output. What gopy produces from the same source.

Open the issue at github.com/tamnd/gopy/issues with all three. Tracebacks, return values, exception messages, and bytecode diffs all qualify.

How a parity port lands

The typical flow for porting a CPython subsystem to gopy:

  1. Pin the CPython source. The work targets a specific 3.14.x tag in a local ~/cpython-314 mirror.
  2. Function-by-function translation. Each Go function gets a // CPython: file.c:Lnn FuncName citation. No invented shims.
  3. Golden corpus. A test corpus is generated from CPython and frozen as the expected output.
  4. Spec entry. The work has an entry under website/docs/specs tracking the checklist of functions ported, tests passing, and remaining gaps.
  5. CI gate. The spec's checklist promotes from "in progress" to "shipped" only when the gate test passes against the golden corpus.

This is slower than freehand reimplementation, but it is the only way to keep behaviour aligned across hundreds of files.