pytogo — a fast, crude, and incomplete (but useful) Python to Go translator


pytogo <python-source >go-source


pytogo is a fast, crude, incomplete Python-to-Go translator meant to assist a human programmer in translating large volumes of Python to idiomatic Go, preserving comments and structure. It does the mechanical things that are easy for software but hard for humans, leaving a human free to concentrate on the hard parts like type annotations.

It exists because at the time it was written the only automated translators available either produced rather horrifyingly unidiomatic generated Go or wouldn’t translate standalone Python code to standalone Go, requiring a heavyweight support package.

One of the main thing this tool does is deduce open and close braces from Python indentation with ":" and leading whitespace, a tedious and error-prone task for humans.

It is conservative, never actually throwing away information from the input Python. But it assumes its Python input is well-formed, and can have point failures on inputs that are sufficiently weird. If your Python has no syntax errors it is probably well-formed enough.

Here are some things to do before running this tool. The first three steps preserve the correctness of the Python.

  1. If your code does not use a PEP8-standard 4-space indent, you should reformat it to do so before applying this tool. Tabs are treated as equivalent to 8 spaces. The output will retain 4-space indents and should be fixed up with "go fmt" or equivalent.
  2. It advisable to decorate your Python code with PEP484 function type annotations (which pytogo can use directly) before applying pytogo. Without these pytogo must fall back on its own rules, which are rather weak.
  3. Find all instances of short-form ifs and whiles that have their entire block on the same line as the control construct, e.g. "if count == 0: do_special_thing()". These confuse the indent tracker. Insert the whitespace to make them regular guarded blocks with an indent; this will greatly reduce the amount of hand-correction you need to apply to the generated Go.
  4. The following other Go constructions can also confuse the indent tracker: "with", "try/finally", and "try/except". Consider commenting out with, try and except lines with an indication of the indent level.

Here are the supported transformations:

  • Opens Python block scopes with { and closes them with }.
  • Changes docstrings to comments. The first line of a function’s docstring is moved to the godoc-preferred position just above the function signature line.
  • Changes comments from #-led to //-led
  • Translates Python boolean constants True, False to Go true, false; alse Python None to Go nil.
  • Translates Python "in range" to Go ":= range". Some common for-loops using range that iterate over lists and maps are translated; others you will need to fix up by hand.
  • Maps Python single-quote literals to Go double-quote literals. Python r-prefix string literals and multiline strings become Go backtick literals You’ll need to fix up backslash escapes in multiline literals yourself.
  • Translates Python logical connectives and or not to Go && || !.
  • Changes "def " to "func ".
  • "= 1" and "-=1" become + and --.
  • Common cases of various Python string library methods - capitalize(), count(), endswith(), find(), join(), lower(), lstrip(), rfind(), replace(), rstrip(), split(), startswith(), split(), upper() - are translated to Go string library calls.
  • Some common Python standard library calls and variables, notably from os and os.filepath and sys, are translated into Go equivalents. The result is not guaranteed to be perfectly correct; a Python call that throws a signal on failure may map into a Go function with an error return. But it will always be a step in the right direction, and the Go compiler will respond with indicative error messages.
  • math and cmath library calls are also translated; Python float is assumed to map to Go float64 and complex to complex128. The Go compiler will refuse to do certain implicit conversions that Python takes for granted, and will throw errors; you must fix those up by hand.
  • The append() method of Python is translated to a call to Go’s append intrinsic.
  • Python’s "del foo[bar]" is translated to Go "delete(foo, bar)". Note that this can false-match on a list del with a numeric key; this will throw a compilation error in Go and you can hand-fix it.
  • Trailing line-continuation backslashes are removed.
  • Python try/finally/catch become pseudo-statements with delimited block scopes. You will need to fix these up by hand.
  • Single-line % expressions with a string-literal left hand and either tuple or single-variable right hands are translated into fmt.Sprintf calls. You will need to translate %-cookies by hand; %r → %v or %q is the usual trouble spot.
  • Many, but not necessarily all, cases where Go ":=" assignment can replace a plain "=" assignment in Python are translated.
  • If your function signatures have type hints in PEP484 format, pytogo will use them to create Go function signatures. pytogo knows that str should map to Go string, float to float64, and complex to complex128; other types it leaves alone.
  • In the absence of PEP484 hints, pytogo will deduce some return types. The rules for this are weak and only work on functions for which the type of an expression in a return statement can be deduced by pattern-matching on the syntax. They will fail when a function can return any of several types.
  • The Python "*args" form for variadics is translated as Go "args …".
  • A recognizable leading Python shebang line is convered to "package main".
  • Import lines are massaged into a Go-like form. Some recognizable Python librarie names are either (a) mapped to the corresponding Go library (if there is one), or discarded (if there is not). The remainder are passed through unaltered and must be fixed up by hand.
  • Changes if statements testing for a literal string in an object reference to call the string.Contains() function. It does not, however, nandle list or tuple literals on the right-hand side.

Things it doesn’t do:

Know anything about Python class structure, or translate it. Handle iterators, lambda, filter, comprehensions, decorators, raise statements, or operator overloading. Translate dictionary arguments, translate PEP484 type aliases or interpret PEP484 type-composition syntax.

It is probably best used by filtering sections of files for incremental conversion rather than trying to do entire files at once.


Report bugs to Eric S. Raymond <>. The project page is at