SYNOPSIS

pytogo <python-source >go-source

DESCRIPTION

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.

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

  1. Have a strong test suite. Run it before starting, and after each preparation step to check that you have a working Python program.

  2. 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.

  3. Move any print statements you still have in Python 2 format (no parens around the argument) to Python 3 format. If your base code is Python 2, apply the "from future import print_function" special import.

  4. Goify function and variable names by removing underscores. Rather than simply smashing them out, you probably wamt to replace _x by X for each letter X, producing biCapitalized identifiers that go lint won’t object to.

  5. It is 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. Note that this implies giving up on Python 2 compatibility - you can remove the future import you may have added in an earlier step.

  6. Comments following the trailing ":" on a control-construct line wil also confuse pytogo. Move them to avoid trouble.

  7. The following other Python 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 converted to "package main".

  • Import lines are massaged into a Go-like form. Some recognizable Python library 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, handle 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. Does not translate Python backslash references in substitution strings to Go-sytyle {} cookies.

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

BUGS

See the preparation steps above for Python cases that can trip you up. In addition to those, the following bugs are known:

If your Python code has a double-quoted string literal ending in "r", and there is another double-quoted string literal following it on the same line, the code for translating such literals will false-match and garble the code between the literals.

A "#" inside a Python docstring will be turned into a Go //.

If a docstring is immediately followed by Python comments it will turn into an ordinary Go comment.

Report bugs to Eric S. Raymond <esr@thyrsus.com>. The project page is at http://catb.org/~esr/pytogo