The much-hyped prospect of WebAssembly hasn’t really panned out yet. There has been progress, and someday we’ll probably get to the point where Python and other languages can be ‘compiled’ directly to WebAssembly — but right now the tools and processes for doing it are pretty clunky and the performance of Python intepreters running in the browser remains unimpressive. The most successful project in this space — Pypy.js works pretty well. But, because it’s a port of the whole CPython interpreter, it’s a huge download: on my fast connection it takes 11 seconds to load and boot the Pypy.js interpreter for the first time. Pypy.js is a hopeful sign, since it’s extremely compatable and, startup times excepted, not too slow to run. However it’s going to be a while yet before somebody figures out how to cut down the huge cost of importing a whole python ecosystem into a browser.
You can tell it’s a real project because it’s got merch!
The most irritating part of using a transpiler is debugging. By definition, there’s not a 1:1 match between the code you wrote and the code that’s being run, which usually makes it tricky to understand how things have gone wrong.
Transcrypt makes this far less painful by generating good source maps, which provide a map between the compiled code and the source. This feature was created to support ‘minified’ JS (where all the whitespace is removed to allow for quicker downloads), but Transcrypt hijacks it so that runtime problems in the JS version point to the python source when debugging. In Chrome, at least, I can even get live preview of the values at a breakpoint:
Debugging Pysteroids.py in Transcrypt and Chrome.
For me this is Transcrypt’s killer advantage over alternate transpilers: it means I can debug as well as code in Python.
Array) and dicts (= JS
Object) have related but still distinct behaviors. This forces the transpiler designer to choose between re-implementing the Python behavior in a custom JS object — which is going to be slower than using the native JS types — and creating false-friend code which confounds the user’s expectations when run in the browser. Rapydscript basically followed the designer’s intuitions as to which behaviors to keep and which to branch, so perhaps it’s not surprising that this has led to a rival fork of the project from a developer who disagreed with some of those choices. All translation involves compromises: as they used to say “traddutore tradditore”, “a translator is a traitor.”
Transcrypt tries to resolve this tension by allowing more local control over the compilation strateg. You can opt in to the more expensive or intrusive features using compiler directives. This moves more of the burden onto you, but it at least allows the choices to be made tactically rather than making you work in a predetermined way.
So, for example, an ordinary Transcrypt function does not accept the
**kwards syntax. However you can enable it in functions where it’s appropriate like this:
def vanilla_function(*args): print ("*args are supported automatically") __pragma__('kwargs') def kwargs_function(*args, **kwargs): if kwargs['test']: print ("kwargs have to be enabled manually") __pragma__('nokwargs')
I could be happier about the aesthetics of all those
__pragma__ statements lying around but at least they are explicit. One quirk I did find a bit tough is that it’s not always clear where the
__pragma__ really wants to reside: so, for example, I wrote a litte coroutine generator that needed to use the
send() feature of generators. In Transcrypt, that is only enabled with a pragma, but it took me a couple of hourse of confusion before I realized the pragma had to be present where I called
send(), not when I created the generator.
Nevertheless, I do like the idea of having opt-in control over the more expensive or complex features of Python. Not only does it let the user make personal tradeoffs between Pythonicism and performance, it also helps to teach you where the real differences betwee pure Python and Transcrybed Python lie.
This aspect is subjective, but in general I’d say Transcrypt feels more “pythonic” than Rapydscript. The compiler is actually written in Python and uses the native Python AST module, so it does not have to reinvent the semantics of the Python side of the language. It’s got most of the things that make Python a high-productivity environment — classes, list comprehensions, decorators, even metaclasses. I’ve run into a few gaps in the toolkit — for example, at present a metaclass can’t create an instancemethod for you — but I’d say that at least 9 out of every ten things I try using my Python instincts work as expected.
print (1 + "X")
will raise an error:
TypeError: unsupported operand type(s) for +: 'int' and 'str'
There are also parts of Python that haven’t been ported. The two that I’ve been bummed out by are context managers and descriptors, both of which I miss. I’ve bumped into the limits of the port once or twice as well — for example the Transcrypt
zip() works as expected for static lists but doesn’t work with iterators or generators. On the plus side Transcrypt is open source so it’s not unlikely that some of these gaps will get filled. However it’s important to recognize that Transcrypt isn’t intended to be a complete Python: the project’s goal to combine high — but not complete — compatibility with speed and simplicity. Completists will probably find it frustrating, but pragmatists will probably enjoy what it can already do.
Despite the missing pieces, I like Transcrypt pretty well so far. It’s not a complete web-ready Python, but it’s a lot more fun and productive for me than writing JS. It only took a few nights to bang out a version of Asteroids with Transcrypt and three.JS which runs pretty smoothly on my phone with 3d graphics and audio. You can play it here (note that I didn’t push it too far — you’ll need to reload the page after you clear the level or die three times. My high score so far is 9157, if you’re feeling competitive).
I don’t think I’d be substantially more worried about tackling a bigger project in Transcrypt than I would be to do it in JS, though that probably says more about my lack of JS-fu than anything else. On the whole, it’s a fun and useful piece of kit. Although the missing bits and pieces are irritating, I’m not altogether sure they’re actually more irritating than the hassle of creating a working virtualenv with pyglet or pygame would be. And — unlike pyglet or pygame — once I’ve got my project compiled I can get it into lots of people’s hands by simply popping it onto a web server without any of the usual indignities of Python distribution.
So… it’s not quite web-python Nirvana but it’s pretty damn cool.