Brython: Python for the Browser
Brython is a browser-based implementation of Python 3 and transpiler that has lofty goals for the browser. It doesn't seek to live side by side with JavaScript, but wants to supplant it as the scripting language of the web. In this article, I'll investigate how it works and stacks up to JavaScript through several small examples.
What Is a Transpiler?
A transpiler, or source-to-source compiler, translates source code from one language into another. Some of the more commonly known transpilers are CoffeeScript and emscripten, which both generate source code for JavaScript. emscripten generates its JavaScript from LLVM bitcode (typically compiled from C or C++). The physics simulation library, ammo.js, was converted from the C++ Bullet library with emscripten. Brython is also considered to be a transpiler.
You might use such a language for several reasons. You might know the source language better than the target language. On the other hand, the source language might allow you to be more expressive or write more terse code than the target language. Coming from a Java and Groovy background, I've found CoffeeScript to better fit how I like to write code for my pet projects, and it allows me to get a lot done with small amounts of code. The quality of the JavaScript code that CoffeeScript generates is code that I'd be proud to commit.
Getting Started with Brython
Brython has two types of distributions: a development build and a deployment build. The development build is an archive of the project's website. It's useful during the early stages because you can modify the included examples or bang out some code in the Brython console. The deployment build contains only brython.js, a number of python files providing core language features, and a set of JavaScript files that provide HTML-specific features to Brython.
Working with HTML
Brython has first-class support for HTML elements. After importing the html module, you have native access to all the HTML4 and HTML5 tags. We instantiate an element by calling the full name in all caps. You can include an option innerText string and an attribute list. Below we have an anchor that has 'Python' as innerText and navigates to python.org. The next link in the snippet is an alias for element.appendChild.
link = html.A('Python', href='http://www.python.org') doc <= link
The following is a simple script that changes the address an anchor tag links to. One of the fun things about using Brython is that you have a shortcut to document.getElementById, thanks to the doc object. You can query by id by calling doc["elemId"] or by tag name with doc[TAGNAME]. Below is an example instantiating a canvas and setting some properties.
canvas = html.CANVAS() canvas.height = 480 canvas.width = 640 ctx = canvas.getContext('2d') doc['gameboard'] <= canvas
Working with Existing JS Objects
The number of available libraries in JavaScript expectedly far outstrips the number of Brython libraries. There's a class called JSONObject that allows you to coerce an object from JavaScript space to Brython. Take, for instance, a JavaScript foo object; we would use it in Brython as follows:
<script type="text/javascript"> var foo = new Foo(); </script> <script type="text/python"> foo = JSObject(foo) foo.bar() </script>
You can even call arbitrary JavaScript code using the JSObject by embedding an eval call, as shown below. Evals are messy, so you probably should only use them in extreme cases.
a = JSObject(eval("String('123s')"))
Creating a Simple Canvas Game/Project
To assess how Brython would work in a real-world situation, I decided to port one of my smaller Canvas2D demos to Brython, a binary clock originally made using Amino.js. Check out my article for more information.
The subset of Python 3 is decently powerful, but I was missing a couple of things to port the app. The first item I had to replace was string padding. Python has a function called zfill that does left padding of string, but it is not supported in Brython. JavaScript doesn't have such a function at all, so I just ported the JavaScript version to Brython.
def lpad(s, length): while (len(s) < length): s = "0" + s return s
The next thing I had to deal with was the lack of base encoding. JavaScript allows you to encode to an arbitrary base in its toString method. Python has no such method, and you have to code it by hand. Luckily, I happened upon baseconv. With minimal changes (dropping the executable script stuff), I was able to get it working in the browser.
Porting the project was fairly straightforward. One of the only sticking points is that Python/Brython is very particular about how you reference properties of a dictionary, Python's version of a Map. Whereas JavaScript allows you to either map_(obj.['prop']) notation or dot notation _(obj.prop) to dereference a property, if you declare an object, you must use dot notation. If you created a dictionary, you must use map notation.
Partially due to the lack of curly braces, the Brython code ended up being approximately 30% smaller than the JavaScript code.
Conclusion
In this article, we explored Brython, a source-to-source compiler that allows you to write Python code in the browser. Brython's mission statement is that it aims to replace JavaScript as the scripting language of the web. In that fight, it has a long way to go. The allure of being able to reuse a subset of server code in the browser is definitely compelling.
I'm interested in using Brython with some hobby programming projects, but I couldn't recommend it for the workplace just yet. Brython has a core that's written in JavaScript with most modules in Python. As such, even for the small binary clock example, Brython had to load four files in addition to brython.js. And two of those files needed to be interpreted at runtime adding a bit of overhead.
Luckily, the development distribution includes scripts to pre-compile source to JavaScript; however, this is something best left to the final stages of development. I found the generated JavaScript almost impossible to debug and not something that I would want to check into source control in its raw state. That being said, if you love Python and already use it as a web stack, Brython is definitely worth a look.